专栏首页Liusy01JVM垃圾回收机制

JVM垃圾回收机制

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。

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

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

本文分享自微信公众号 - Liusy01(Liusy_01),作者:Liusy

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-11-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 第二篇:JVM内存结构和Java内存模型

    其实并不想写这一篇文章,原因是这个东东只要是Java开发者都知道的内容,大部分都是偏理论性的,但是为了方便后续文章的开展,所以还是着手描写一下我个人对JVM的了...

    Liusy
  • 【设计模式-中介者模式】

    【导读】在信息高速发展的时代,估计很多朋友都记不住别人的号码,这个时候就需要一个“通讯录”来存储号码,这个通讯录就是中介者。

    Liusy
  • List源码解析

    开始看一下集合Collection,List是Collection的一个子接口,其是很常用的,主要是看一下其下的几个类。

    Liusy
  • JVM初探 -JVM内存模型

    Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存...

    哲洛不闹
  • JVM参数配置 java内存区域

           http://www.importnew.com/18694.html

    江湖前辈黄药师
  • 理解JVM垃圾回收的机制

    其中(1)和(2)我们在之前已经介绍过了,今天我们来学习一下关于JVM垃圾回收(Garbage Collection)的内容:

    我是攻城师
  • JVM层GC调优(上)

    JVM层的GC调优是生产环境上必不可少的一个环节,因为我们需要确定这个进程可以占用多少内存,以及设定一些参数的阀值。以此来优化项目的性能和提高可用性,而且这也是...

    端碗吹水
  • JVM常见垃圾回收算法

    jdk1.7.0_79   众所周知,Java是一门不用程序员手动管理内存的语言,全靠JVM自动管理内存,既然是自动管理,那必然有一个垃圾内存的回收机制或者回收...

    用户1148394
  • JVM学习第二天(垃圾回收器和内存分配策略)大章

    说道垃圾回收器大家应该都会有所了解,GC白,当然说道具体的可能就不是很清楚了,今天我们就来玩一玩;

    彼岸舞
  • 面试必会:JVM堆内存详解

    年轻代中包含两个区:Eden 和survivor,并且用于存储新产生的对象,其中有两个survivor区

    公众号 IT老哥

扫码关注云+社区

领取腾讯云代金券