JVM垃圾收集机制

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM垃圾收集机制相关的知识,希望对你有一定的参考价值。

参考技术A

JVM垃圾回收机制是java程序员必须要了解的知识,对于程序调优具有很大的帮助(同时也是大厂面试必问题)。

要了解垃圾回收机制,主要从三个方面:

(1)垃圾回收面向的对象是谁?

(2)垃圾回收算法有哪些?

(3)垃圾收集器有哪些?每个收集器有什么特点。

接下来一一讲解清楚:

一、垃圾回收面向的对象

也就是字面意思, 垃圾 回收嘛,重要的是垃圾,那什么对象是垃圾呢,简单来说就是无用的或已死的对象。这样又引申出来什么对象是已死的,怎么判断对象是否已死?

判断对象是否已死有两种算法和对象引用分类:

(1)引用计数算法:

也是字面意思,通过给对象添加引用计数器,添加引用+1,反之-1。当引用为0的时候,此时对象就可判断为无用的。

优点:实现简单,效率高。

缺点:无法解决循环引用(就是A引用B,B也引用A的情况)的问题。

(2)根搜索算法(也就是GC Roots):

通过一系列称为GC Roots的对象,向下搜索,路径为引用链,当某个对象无法向上搜索到GC Roots,也就是成为GC Roots不可达,则为无用对象。

如果一个对象是GC Roots不可达,则需要经过两次标记才会进行回收,第一次标记的时候,会判断是否需要执行finalize方法(没必要执行的情况:没实现finalize方法或者已经执行过)。如果需要执行finalize方法,则会放入一个回收队列中,对于回收队列中的对象,如果执行finalize方法之后,没法将对象重新跟GC Roots进行关联,则会进行回收。

很抽象,对吧,来一个明了的解释?

比如手机坏了(不可达对象),有钱不在乎就直接拿去回收(这就是没实现finalize方法),如果已经修过但是修不好了(已经执行过finalize方法),就直接拿去回收站回收掉。如果没修过,就会拿去维修店(回收队列)进行维修,实在维修不好了(执行了finalize方法,但是无法连上GC Roots),就会拿去回收站回收掉了。

那什么对象可以成为GC Roots呢?

1>虚拟机栈中的引用对象

2>本地方法栈中Native方法引用的对象

2>方法区静态属性引用对象

3>方法区常量引用对象

(3)对象引用分类

1>强引用:例如实例一个对象,就是即使内存不够用了,打死都不回收的那种。

2>软引用:有用非必须对象,内存够,则不进行回收,内存不够,则回收。例如A借钱给B,当A还有钱的时候,B可以先不还,A没钱了,B就必须还了。

3>弱引用:非必须对象,只能存活到下一次垃圾回收前。

4>虚引用:幽灵引用,必须跟引用队列配合使用,目的是回收前收到系统通知。

下面是java的引用类型结构图:

(1)软引用示例

内存够用的情况:

运行结果:

内存不够用的情况:

运行结果:

(2)弱引用示例结果:

无论如何都会被回收

(3)虚引用示例:

运行结果:

解释:为什么2和5的输出为null呢,如下

3为null是因为还没有进行gc,所以对象还没加入到引用队列中,在gc后就加入到了引用队列中,所以6有值。

这个虚引用在GC后会将对象放到引用队列中,所以可以在对象回收后做相应的操作,判断对象是否在引用队列中,可以进行后置通知,类似spring aop的后置通知。

二、垃圾回收发生的区域

垃圾回收主要发生在堆内存里面,而堆内存又细分为 年轻代 老年代 ,默认情况下年轻代和老年代比例为1:2,比如整个堆内存大小为3G,年轻代和老年代分别就是1G和2G,想要更改这个比例需要修改JVM参数-XX:NewRatio,

比如-XX:NewRatio=4,那老年代:年轻代=4:1。而年轻代又分为Eden区,S0(Survivor From)和S1(Survivor To)区,一般Eden:S0:S1=8:1:1,如果想要更改此比例,则修改JVM参数-XX:SurvivorRatio=4,此时就是Eden:S0:S1=4:1:1。

在年轻代发生GC称为Young GC,老年代发生GC成为Full GC,Young GC比Full GC频繁。

解析Young GC:

JVM启动后,第一次GC,就会把Eden区存活的对象移入S0区;第二次GC就是Eden区和S0一起GC,此时会把存活的对象移入S1区,S0清空;第三次GC就是Eden区和S1区进行GC,会把存活的对象移入S0区,如此往复循环15次(默认),就会把存活的对象存入老年区。

类似与如果有三个桶,编号分别为1(1号桶内的沙子是源源不断的,就像工地上。你们没去工地搬过砖可能不知道,但是我真的去工地上搬过啊),2,3。1里面装有沙子,需要将沙子筛为细沙。首先将桶1内的沙子筛选一遍过后的放置于桶2,第二次筛选就会将桶1和桶2里面的沙子一起筛,筛完之后放到桶3内,桶2清空。第三次筛选就会将桶1和桶3的沙子一起筛选,晒完放到桶2内,桶3清空。如此往复循环15次,桶2或桶3里面的沙子就是合格的沙子,就需要放到备用桶内以待使用。

上述中桶1就是Eden区,桶2就是S0区,桶3就是S1区。

三、垃圾回收算法

三种,分别是复制算法,标记-清除算法,标记-整理算法。

(1)复制算法。

其会将内存区域分成同样大小的两块,一块用来使用,另外一块在GC的时候存放存活的对象,然后将使用的一块清除。如此循环往复。

适用于新生代。

优点:没有内存碎片,缺点:只能使用一般的内存。

(2)标记-清除算法。

使用所有内存区域,在GC的时候会将需要回收的内存区域先进行标记,然后同意回收。

适用于老年代。

缺点:产生大量内存碎片,会直接导致大对象无法分配内存。

(3)标记-整理算法。

使用所有内存区域,在GC的时候会先将需要回收的内存区域进行标记,然后将存活对象忘一边移动,最后将清理掉边界以外的所有内存。

适用于老年代。

四、GC日志查看

利用JVM参数-XX:+PrintGCDetails就可以在GC的时候打印出GC日志。

年轻代GC日志:

老年代GC日志:

五、垃圾收集器

主要有四类收集器以及七大收集器

四类:

(1)Serial:单线程收集器,阻塞工作线程,它一工作,全部都得停下。

(2)Paralle:Serial的多线程版本,也是阻塞工作线程

(3)CMS(ConcMarkSweep):并行垃圾收集器,可以和工作线程一起工作。

(4)G1:将堆分成大小一致的区域,然后并发的对其进行垃圾回收。

怎么查看默认的收集器呢?

用JVM参数-XX:+PrintCommandLineFlags,运行之后会输出如下参数。可以看到,jdk1.8默认是Parallel收集器。

七大收集器:

(1)Serial:串行垃圾收集器,单线程收集器。用于新生代。用JVM参数-XX:+UseSerialGC开启,开启后Young区用Serial(底层复制算法),Old区用Serial Old(Serial的老年代版本,底层是标记整理算法)。

(2)ParNew:用于新生代,并行收集器。就是Serial的多线程版本。用JVM参数-XX:+UseParNewGC,young:parnew,复制算法。Old:serialOld,标记整理算法。-XX:ParallecGCThreads限制线程回收数量,默认跟cpu数目一样。只是新生代用并行,老年代用串行。

(3)Parallel Scavenge:并行回收收集器。用JVM参数-XX:+UseParallelGC开启,young:parallel scavenge(底层是复制算法),old:parallel old(parallel的老年代版本,底层是标记整理),新生代老年代都用并行回收器。

这个收集器有两个优点:

可控的吞吐量 :就是工作线程工作90%的时间,回收线程工作10%的时间,即是说有90%的吞吐量。

自适应调节策略 :会动态调节参数以获取最短的停顿时间。

(4)Parallel Old:Parallel Scavenge的老年代版本,用的是标记整理算法。用JVM参数-XX:+UseParallelOldGC开启,新生代用Parallel Scavenge,老年代用Parallel Old

(5)CMS(ConcMarkSweep):并发标记清除。 底层是标记清除算法,所以会产生内存碎片,同时也会耗cpu。 以获取最短回收停顿时间为目标。-XX:+UseConcMarkSweep,新生代用ParNew,老年代用CMS。CMS必须在堆内存用完之前进行清除,否则会失败,这时会调用SerialOld后备收集器。

初始标记和重新标记都会停止工作线程,并发标记和并发清除会跟工作线程一起工作。

(6)SerialOld:老年代串行收集器(以后Hotspot虚拟机会直接移除掉)。

(7)G1:G1垃圾收集器,算法是标记整理,不会产生内存碎片。横跨新生代老年代。实现尽量高吞吐量,满足回收停顿时间更短。

G1可以精确控制垃圾收集的停顿时间,用JVM参数-XX:MaxGCPauseMillis=n,n为停顿时间,单位为毫秒。

区域化内存划片Region,会把整个堆划分成同样大小的区域块(1MB~32MB),最多2048个内存区域块,所以能支持的最大内存为32*2048=65535MB,约为64G。

上图是收集前和收集后的对比,有些对象很大,分割之后就是连续的区域,也即是上图的Humongous。

上述理论可能有点乏味,下图很清晰明了(某度找的)。

下面来一张整个垃圾回收机制的思维导图(太大,分成两部分)。

=======================================================

我是Liusy,一个喜欢健身的程序猿。

欢迎关注【Liusy01】,一起交流Java技术及健身,获取更多干货。

以上是关于JVM垃圾收集机制的主要内容,如果未能解决你的问题,请参考以下文章

JVM垃圾处理机制

Java垃圾回收机制是啥?

JVM的垃圾回收机制 总结(垃圾收集回收算法垃圾回收器)

JVM的垃圾回收机制 总结(垃圾收集回收算法垃圾回收器)

JVM详解——垃圾回收算法

JVM技术专题 深入理解G1垃圾收集器的原理和运行机制「原理分析篇」