Java垃圾回收机制(GC)

二、 Java垃圾回收机制(GC)

1.垃圾回收的标记算法(即判定对象是否为垃圾的算法):

对象被判定为垃圾的标准:没有被其他对象引用

1)引用计数算法:

即通过判断对象的引用数量来决定对象是否可以被回收;

每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1;任何引用计数为0的对象实例是可以被当做垃圾收集的。

优点:执行效率高,程序执行受影响小;

缺点:无法检测出循环引用的情况(如:父对象对子对象有一个引用,子对象也对父对象有一个引用),导致内存泄漏。

2)可达性分析算法:

即通过判断对象的引用链是否可达来决定对象是否可以被回收;

它是从图论引入,从GC Root开始向下搜索引用,看是否有可达对象;搜索走过的路径就叫做引用链;当一个对象从GC Root开始没有任何引用相连,则这个对象是不可达的,即不可用的,它可以被标记为垃圾,被回收。

可以作为GC Root的对象:

虚拟机栈中引用的对象(栈帧中的本地变量表);

方法区中的常量引用的对象;

方法区中的类静态属性引用的对象;

本地方法栈中JNI(Native方法)的引用对象;

活跃线程的引用对象;

2. 谈谈你了解的垃圾回收算法:

1)标记清除算法(Mark and Sweep)

标记:从根集合进行扫描,对存活的对象进行标记(即利用前面的可达性分析算法进行标记);
清除:对堆内存从头到尾进行线性遍历,回收不可达对象内存;

如下图:(标记清除算法)

Java垃圾回收机制(GC)_第1张图片

缺点:内存碎片化(如果较大的对象存入时,由于没有连续的剩余内存,需要启动GC)

2) 复制算法(Copying)

将可用的内存分别对象面和空闲面;对象在对象面上创建;
开启垃圾回收时,将对象面上存活的对象复制到空闲面;
再把对象面上已使用的空间一次清除

优点:
适用于对象存活率低的场景,如年轻代;
解决了碎片化问题;
顺序分配内存,简单高效。

缺点:应对对象存活率较高的场景就力不从心了,效率较低;更重的的它需要浪费50%的空间。

3)标记-整理算法(Compacting)

在标记清除算法的基础上进行了移动,因此成本更高,但是解决了碎片化问题;

标记:从根集合进行扫描,对存货的对象进行标记;
清除:移动所有存活的对象,且按照内存地址次序排列,然后将末端内存地址以后的内存全部回收。

优点:
避免了内存的不连续性;
不用设置两块内存互换;
适用于存活率较高的场景(如老年代)

4)分代收集算法(Generational Collector)

是垃圾回收算法的组合拳;按照对象的生命周期的不同划分区域,以采用不同的垃圾回收算法;

目的:提高JVM的回收效率

JDK8之后,永久代被取消,堆被划分为年轻代和老年代,其中年轻代适合使用复制算法回收垃圾,而老年代适合使用标记-整理算法回收垃圾。

GC的分类:

(1)Minor GC: (当Eden区和survivor区空间不足,会触发Minor GC)

(2)Full GC: (当老年代空间不足,或者调用了System.gc()时,会触发Full GC)

  • 年轻代: 尽可能快速地收集掉那些生命周期短的对象;

年轻代被划分为Eden区和两个Survivor区,一般的比例是8:1:1,采用复制算法回收垃圾,如下图(年轻代和老年代):

当Eden区满了之后,会触发一次Minor GC: 将存活的对象复制到其中的一个survivor区(假设A区),且该存活对象的生命周期加1,然后回收Eden区;

当Eden区第二次满了之后,会再次触发Minor GC: 将Eden区和survivor区(即A区)的存活对象复制到另一个survivor区(假设为B区),并且将它们的生命周期均加1,然后回收Eden区和A区;

… …; 周而复始,当survivor区中对象的生命周期达到一定值(默认情况下为15,可以通过参数-XX:+MaxTenuringThreshold设置),或者当新建的对象太大时,会将这些对象存放在老年代。

年轻代的对象如何晋升到老年代:

(1)经历一定Minor次数依然存活的对象;

(2)survivor区中存放不下的对象

(3)新生成的大对象(-XX:+PretenuerSizeThreshold)

常见的调优参数:

-XX:SurvivorRatio: Eden和Survivor的比值,默认为8:1;

-XX:NewRatio: 老年代和年轻代的内存大小的比例,默认为2:1;

-XX:MaxTenuringThreshold: 对象从年轻代晋升到老年代经过GC次数的最大阈值;

-XX:PretenuerSizeThreshold: 新生的大对象。

  • 老年代: 存放生命周期较长的对象;

一般采用标记-清除算法、和标记-整理算法;

Full GC 和 Major GC(需要Major GC是上面的Full GC,还是整个堆上的垃圾回收);

Full GC 比 Minor GC 慢,但执行频率低;

触发Full GC 的条件:

(1)老年代空间不足;

(2)永久代空间不足(JDK1.7之前);

(3)调用System.gc(); (注意,这只是一个提醒作用,开发人员不能控制GC)

(4)CMS GC 时出现 promotion failed,concurrent model failure(即 )

(5)Minor GC晋升到老年代的平均大小大于老年代的剩余空间;

(6)使用RMI来进行RPC或管理的JDK应用,每小时执行1次Full GC;

3.新生代垃圾收集器:

Stop-the-World: 即JVM由于要执行GC而停止了应用程序的执行;任何一种GC算法中都会发生;

多数GC优化通过减少Stop-the-World发生的时间来提高程序性能,从而使程序"高吞吐,低停顿"。

Safepoint: 分析过程中对象引用关系不会发生变化的点;

产生Safepoint的地方:方法调用、循环跳转、异常跳转等;

安全点数量的适中

JVM的运行模式:

Server: 启动时速度慢,启动重量级的JVM,当稳定运行后速度快;

Client:启动时速度快,启动轻量级的JVM,当稳定运行后速度慢;

注:可以使用java -version查看当前JVM的运行模式

垃圾收集器之间的联系,如图(垃圾收集器):

Java垃圾回收机制(GC)_第2张图片

1)Serial收集器(-XX:UseSerialGC,复制算法):

单线程收集,进行垃圾收集时,必须暂停所有工作线程;(停顿几十毫秒或者几百毫秒)

简单高效,Client模式下默认的年轻代收集器。

2)ParNew收集器(-XX:UseParGC,复制算法)

多线程收集,其余的行为、特点和Serial收集器一样;

单核执行效率不如Serial,在多核下执行才有优势(Server模式下)

3)Parallel Scavenge收集器(-XX:UseParallelGC,复制算法):

吞吐量=运行用户代码的时间/(运行用户代码的时间+垃圾回收的时间)

比起关注用户线程停顿时间,更关注系统的吞吐量;

在多核下执行才有优势,Server模式下默认的年轻代收集器。

(-XX:UseAdaptiveSizePolicy)

4.老年代常见的垃圾收集器

1)Serial Old收集器(-XX:UseSerialOldGC,标记-整理算法):

单线程收集,进行垃圾收集时,必须暂停所有工作线程;(停顿几十毫秒或者几百毫秒)

简单高效,Client模式下默认的老年代收集器。

2)Parallel Old收集器(-XX:UseParallelOldGC,标记-整理算法):

多线程,吞吐量优先;

3)CMS收集器(-XX:UseConcMarkSweepGC,标记-清除算法):老年代垃圾收集器的半壁江山

Step1: 初始标记 stop-the-world

Step2: 并发标记 并发追溯标记,程序不会停顿

Step3: 并发预清理 查找执行并发标记阶段从年轻代晋升到老年代的对象

Step4: 重新标记 暂停虚拟机,扫描CMS堆中的剩余对象

Step5: 并发清理 清理垃圾对象,程序不会停顿

Step6: 重置CMS收集器的数据结构

缺点:碎片化

了解:G1收集器(-XX:UseG1GC,复制+标记-整理算法):

Garbage First 收集器的特点:并发与并行、分代收集、空间整合、可预测的停顿

5. Object的finalized()方法的作用是否与C++的析构函数作用相同?

与C++的析构函数不同,析构函数调用确定(即对象离开作用域后就会被删除),而它的是不确定的;

java中当垃圾回收器要宣告对象死亡时时,至少要经过两次的标记过程:如果对象在进行可达性分析后,没有和GC Root连接的引用链时,就会被第一次标记,并且判断是否执行finalize()方法;如果对象覆盖finalize方法,且未被引用过,这个对象就会被放在F-Queue队列;

方法执行随时可能会被终止;

给予对象最后一次重生的机会。

6.java中的强引用、软引用、弱引用、虚引用有什么用:

1)强引用(Strong Reference)

最普遍的引用:Object obj=new Object()

抛出OutOfMemoryError终止程序也不会回收具有强引用的对象;

通过将对象设置为null来弱化引用,使其被回收。

2)软引用(Soft Reference)

对象处在有用但非必须的状态;

只有当内存空间不足时,GC会回收该引用的对象的内存;

可以用来实现高速缓存。

String str=new String("abc");//强引用
SoftReference softRef=new SoftReference (str);//软引用

3)弱引用(Weak Reference)

非必须的对象,比软引用更弱一些;

GC时会被回收;

被回收的概率也不大,因为GC线程优先级比较低;

适用于引用偶尔被使用且不影响垃圾收集的对象。

String str=new String("abc");//强引用
WeakReference abcWeakRef=new WeakReference (str);//弱引用

4)虚引用(PhantomReference)

不会决定对象的生命周期;

任何时候都可能被垃圾收集器回收;

跟踪对象被垃圾收集器回收的活动,起哨兵作用;

必须和引用队列ReferenceQueue联合使用

String str=new String("abc");//强引用
ReferenceQueue queue=new ReferenceQueue();
PhantomReference ref=new PhantomReference (str,queue);//虚引用

强引用 > 软引用 > 弱引用 > 虚引用

引用类型 被垃圾回收的时间 用途 生存时间
强引用 从来不会 对象的一般状态 JVM停止运行时终止
软引用 在内存不足时 对象缓存 内存不足时终止
弱引用 在垃圾回收时 对象缓存 gc运行后终止
虚引用 Unknown 标记、哨兵 Unknown

引用队列(ReferenceQueue):无实际存储结构,存储逻辑依赖于内部节点之间的关系来表达(head next)

你可能感兴趣的:(Java基础知识,jvm,java)