Bootstrap

JVM深入学习(二)

目录

一.本地方法接口

二.执行引擎

三.垃圾回收

什么是垃圾对象?

垃圾回收发展

哪些区域会出现垃圾回收

内存溢出与内存泄漏

Stop the World

四.垃圾回收阶段算法

1.垃圾标记阶段

  ①引用计数算法

  ②可达性分析算法(根搜索算法)

  finalize()方法对象复活

2.垃圾回收阶段

①标记-复制算法

②标记-清除算法

③标记-压缩(整理)算法

五.垃圾回收器

什么是垃圾回收器?

有哪些垃圾回收期?

垃圾收集器性能指标

G1(Garbage First)

设置垃圾回收器


一.本地方法接口

1.什么是本地方法接口?

        本地方法接口是虚拟机中专门用来调本地方法的接口

2.什么是本地方法?

        在java中被native关键字修饰的方法, 没有方法体, 不是用java语言实现的方法,是用c/c++在操作系统底层实现的方法。

如:①Object 类中的 hashCode()方法,获取对象内存地址,涉及到读取内存。

        ②IO中读文件(输入文件 操作硬盘) read0()方法;

        ③启动线程:native void start0(); 就是把这个线程注册到操作系统。

 3.java中为什么要调用本地方法?

        因为java属于应用层语言,有时候,需要对硬件系统资源进行调用,此时就不方便,而且系统资源不允许应用层程序直接调用,那么就需要通过本地方法调用硬件资源。


二.执行引擎

        执行引擎是虚拟机核心部件之一,主要作用是将加载到虚拟机中的字节码再次转换为机器码 (字节码并不是系统能够直接执行的)。

        执行引擎可以通过解释/编译两种方式将字节码转为机器码。

java程序执行过程中涉及两次编译:

第一次:.java文件(源代码 通过jdk javac调用编译器) -->.class文件, 称为前端编译。

第二次: 通过执行引擎将字节码编译为机器码称为后端编译。

将字节码转为机器码有两种方式:

解释器(解释执行): 对字节码逐行进行解释翻译, 每次执行代码都要解释执行, 效率低。

编译器(编译执行): 编译器会针对执行过程中的热点代码进行编译,并缓存起来, 以后使用时不再需要编译了, 效率高。

为什么要使用解释执行和编译执行并存这样的设计?

        程序开始运行时,解释器可以立即发挥作用,响应速度快,省去编译的时间,直接投入使用。

        而编译器虽然执行速率高,但是前期需要对热点代码进行跟踪和编译,需要消耗时间。两者并存会大大提高代码的运行速率。


三.垃圾回收

什么是垃圾对象?

一个对象不再被任何的引用所指向,没有任何引用指向的对象。

String s = new String();
s = null;

垃圾对象如果不清理, 新的对象可能没有足够空间,可能会导致内存溢出问题.

垃圾回收发展

早期

c/c++语言,内存管理是手动的, 使用时申请,使用完后手动释放。

优点: 对内存管理更加精确,效率高

缺点: 增加程序员负担, 控制不好,容易出事(忘记释放, 误操作内存空间)

后来发展为自动回收:java,C#...都采用自动垃圾回收

优点: 降低程序员负担

缺点: 会占用一些内存空间(垃圾不是出现后立即回收的,具有周期性)。

哪些区域会出现垃圾回收

堆的对象:频繁回收年轻代, 较少回收老年代

方法区:类信息卸载,整堆收集时,会进行回收 FULL GC

内存溢出与内存泄漏

        内存溢出: 内存不够用了

        内存泄漏: 系统中那些用不到的,但是又不能回收的对象

案例: 单例对象,数据库连接对象,IO流,socket这些提供close()的类。

用完之后,如果没有关闭, 垃圾回收器是不能主动回收这些对象的。

        内存泄漏虽然不能直接触发内存溢出, 但是长期有对象不能被回收,也是导致内存溢出的原因之一。 

Stop the World

垃圾回收时,会经历两个阶段: 一是标记阶段二是回收阶段。

在标记和回收时,需要暂停用户线程,如果不暂停标记和回收,可能会出现错标和漏标


四.垃圾回收阶段算法

1.垃圾标记阶段

        将虚拟机中不再被任何引用指向的对象标记出来,在垃圾回收阶段,就会将标记出来对象进行回收。

垃圾标记阶段相关算法

        ①引用计数算法

        (存在缺陷, 没有被虚拟机所使用的)

        设计思想: 在对象中维护一个整数计数器变量,当有引用指向对象时,计数器加一,相反就减一(引用断开)。

        优点: 设计实现简单, 容易分辩对象是否是垃圾对象。

        缺点: 需要维护一个变量存储引用数量, 频繁修改引用计数器变量, 占空间,还耗时。

        最重要的是,无法解决循环引用问题

         循环引用:A引用B,B引用C,C引用A,若外界P引用为“null” 时,这三者互相引用,计数器为1,所以不会被垃圾回收。

        ②可达性分析算法(根搜索算法)

        设计思想: 从一些可以被称为GCRoots的对象开始向下查找,只要某一个对象与GCRoots对象有联系的,可以判断对象时被使用的。与根对象引用链没有任何关系的对象,可以视为垃圾对象。

哪些对象可以作为GCRoots(根对象)?

        1.虚拟机栈中(被调用的方法)所使用的对象

        2.类中的静态属性

        3.虚拟机中使用的系统类对象

        4.所有被同步锁 synchronized 持有的对象

finalize()方法对象复活

        Object类中有一个finalize()方法,这个方法是在对象被回收之前,由虚拟机自动调用的,在对象被回收前,需要执行一些操作,就可以在此方法中编写,finalize()方法可以在子类中重写,finalize()方法只会被调用一次 (第一次被判定为垃圾,要对其回收,调用finalize(), 对象有可能又被引用了,对象就不能被回收,当下一次被判定为垃圾对象时,就不会再调用finalize())。

由于finalize()方法存在, 被标记为垃圾的对象,也不是非死不可的。

可以将对象分为三种状态:

可触及 : 被GCRoots引用的,不是垃圾对象。

可复活的: 被判定为垃圾的,但是finalize()方法还没有被调用过的。

不可触及的: 被判定为垃圾的,finalize() 已经被调用过了。

2.垃圾回收阶段

①标记-复制算法

        将内存可以分为多个较小的块, 当发生垃圾回收时,将一个区域中存活的对象复制到另一个区域,在另一个区域从头开始排列, 清除当前垃圾回收的区域。适合新生代区域。

优点: 清理之后,内存没有碎片。

不足: 回收时,需要移动对象, 所以适合小内存块,而且存活对象少的情况。

②标记-清除算法

        将被标记为垃圾的对象地址进行记录,后面如果分配新对象,判断垃圾对象空间是否能够存储下新的对象。

        如果能存储下,用新对象直接覆盖垃圾对象即可,存活对象是不发生移动的。

        优点: 不会移动对象的。

        不足: 回收后,内存中会出现碎片

③标记-压缩(整理)算法

        存活对象会移动到内存区域的一端,按顺序排列(压缩),清理边界以外的空间。

        在标记清除的基础上进行一次内存整理

        优点: 回收后没有内存碎片。  

标记-清除和标记压缩对比

标记-清除: 不移动存活对象。

标记-压缩: 移动存活对象。

两者都适合于老年代对象回收。

先使用标记-清除,当老年代空间不足,或者不能存储一个较大的对象时,再使用标记压缩算法。

垃圾回收时,根据不同的分区采用不同的回收算法。

新生代: 标记-复制

老年代: 标记-清除 / 标记-压缩


五.垃圾回收器

什么是垃圾回收器?

        垃圾回收期,是对垃圾回收过程实践者(落地),不同的虚拟机中,垃圾回收器种类也是很多的。

有哪些垃圾回收期?

        垃圾回收器分类:

从线程数量上分单线程:垃圾回收线程只有一个。
多线程:有多个垃圾回收线程。
从工作模式上分独占式: 垃圾回收线程执行时,其他用户线程需要暂停(stop the world)
并发式: 垃圾回收线程和用户线程可以做到并发执行
从分区角度上分新生代垃圾回收器
老年代垃圾回收器

垃圾收集器性能指标

①吞吐量,②用户线程暂停时间(重点),③回收时内存开销

垃圾收集器
Serial单线程,新生代收集器
Serial Old单线程,老年代收集器
Parallel Scavenge,ParNew多线程,新生代收集器
Parallel Old多线程,老年代收集器
CMS(并发标记清除收集器)多线程,老年代收集器,开创了垃圾收集线程与用户线程并发执行的先例。

CMS运行方式:

初始标记 -- 独占执行,并发标记 -- 并发执行,重新标记 -- 独占执行,并发清除 -- 并发执行

G1(Garbage First

        G1垃圾回收器,继承了CMS中垃圾收集线程和用户线程并行执行的特点,减少了用户线程暂停的时间。

        同时,将新生代和老年代的各个区域,又划分成各个更小的区域,对每个区域进行跟踪,优先回收价值高的区域(垃圾多的区域,例如可以把伊甸园区可以分成好几个小的区域)。

提升回收效率,提高了吞吐量。不再区分年轻代和老年代,可以做到对整个堆进行回收。

非常适合服务器端程序,大型项目。

设置垃圾回收器

打印默认垃圾回收器

-XX:+PrintCommandLineFlags -version

打印垃圾回收详细信息

-XX:+PrintGCDetails -version

设置垃圾回收器

-XX:+UseG1GC

 感谢你的阅读与关注,如有问题欢迎探讨!💓

;