你不得不知道的JVM 垃圾回收

Posted

tags:

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

参考技术A

一、四种引用方式
1.1 强引用
1.2 软引用(SoftReference)
1.3 弱引用(WeakReference)
1.4 虚引用(PhantomReference)

二、如何判断对象是垃圾
2.1 引用计数法
2.2 根可达性分析

三、垃圾回收算法
3.1 标记-清除(mark-sweep)
3.2 标记-整理(mark-compact)
3.3 标记-复制(mark-copy)

四、垃圾收集器
4.1 分类及特点简述
4.1.1 串行
4.1.2 吞吐量优先
4.1.3 响应时间优先
4.2 串行垃圾回收器详述
4.2.1 Serial
4.2.2 Serial-Old
4.2.3 流程图
4.3 吞吐量优先垃圾回收器详述
4.3.1 JVM相关参数
4.3.2 流程图
4.4、响应时间优先垃圾回收器详述
4.4.1 JVM相关参数
4.4.2 流程图
4.3.3 CMS的特点

五、G1垃圾回收器
5.1 相关JVM参数
5.2 特点
5.3 G1新生代垃圾回收
5.4 G1老年代垃圾回收

只有所有 GC Roots对象都不通过【强引用】引用该对象,该对象才可以被回收。

某个对象只要有一处引用关系,该对象的引用次数就加1,如果一个对象的引用次数为0,则说明该对象是垃圾。

优势:实现简单,效率较高

弊端:如果有一对对象之间形成了相互引用,但是这两个对象都已经没有被其它对象所引用了,正常情况下,这一对对象应该被作为垃圾回收掉,但是因为形成了相互引用导致无法被回收。

通过GC Root对象开始向下寻找,寻找不到的对象即说明没有被引用,那么这些没有被引用的对象被认定为垃圾。

目前,如下对象可以作为GC Root对象:

很好理解,即在GC的放生时候,先对所有对象进行根可达性分析,借此标记所有的垃圾对象;所有对象标记完毕之后会进行清理操作。

因此,总体来说,就是先标记再清除。

弊端;标记清除之后会产生大量不连续的内存碎片,碎片太多可能会导致程序运行过程中需要分配较大对象时,无法满足分配要求导致GC操作。

该回收算法操作过程基本等同于 标记-清除 算法只不过,第二步有点区别,该种方式会在清除的过程中进行 整理 操作,这是最大的不同。

优势:最终不会出现若干空间碎片而导致的空间浪费。

弊端:在整理过程中带来的计算不可小觑。

该种方式与前两种有较大的区别:

该种方式会将存储区分成两个部分,分别为From、To,其中From区域中可能存在着对象,而To区域始终为空,用做下一次接受数据做准备。

分别有两个指针指向这两个区域:From-Pointer、To-Pointer,

优点:这种算法非常适合早生夕死的对象

缺点:始终有一块内存区域是未使用的,造成空间的浪费。

特点:

特点:

特点:

JVM开关:-XX:+UseSerialGC = Serial + SerialOld

上图是:CMS垃圾回收器在老年代GC的工作流程图:

经过上面的文字分析,新生代的Region个数为所有Region个数的5%;这个数值其实是很小的,那么当新生代Region不够用的时候,JVM会划分更多的Region个数给新生代;

当新生代的Region个数占比所有Region个数超过 60% 时,就会进行一次新生代的垃圾回收。

新生代垃圾回收会造成STW。

具体的垃圾回收算法同其它几个新生代垃圾回收器一样,新生代都使用复制算法。

老年代垃圾回收触发机制与参数-XX:InitaingHeapOccupancyPercent有关。

但是需要注意的是:这一次的老年代回收,其实是一次混合垃圾回收,会同时清理新生代、老年代、Humongous。

与新生代回收算法一致,依然使用复制算法,但是垃圾回收的过程等同于老年代响应时间优先的CMS方式

流程分为:

7 种 JVM 垃圾回收器,你知道几个?

前言

今天我们来讲讲jvm最重要的堆内存是如何使用垃圾回收器进行垃圾回收,并且如何使用命令去配置使用这些垃圾回收器。

堆内存详解

上面这个图大家应该已经很明白了吧。大家就可以理解成一个房子被分成了几个房间,每个房间的作用不同而已,有的是婴儿住的,有的是父母住的,有的是爷爷奶奶住的

  • 堆内存被划分为两块,一块的年轻代,另一块是老年代

  • 年轻代又分为Edensurvivor。他俩空间大小比例默认为8:2,

  • 幸存区又分为s0s1。这两个空间大小是一模一样的,就是一对双胞胎,他俩是1:1的比例

堆内存垃圾回收过程

第一步

新生成的对象首先放到Eden区,当Eden区满了会触发Minor GC

第二步

第一步GC活下来的对象,会被移动到survivor区中的S0区,S0区满了之后会触发Minor GC,S0区存活下来的对象会被移动到S1区,S0区空闲。

S1满了之后在GC,存活下来的再次移动到S0区,S1区空闲,这样反反复复GC,每GC一次,对象的年龄就涨一岁,达到某个值后(15),就会进入老年代

第三步

在发生一次Minor GC后(前提条件),老年代可能会出现Major GC,这个视垃圾回收器而定。

Full GC触发条件

  • 手动调用System.gc,会不断的执行Full GC

  • 老年代空间不足/满了

  • 方法区空间不足/满了

注意

们需要记住一个单词:stop-the-world。它会在任何一种GC算法中发生。stop-the-world 意味着JVM因为需要执行GC而停止应用程序的执行。

当stop-the-world 发生时,除GC所需的线程外,所有的线程都进入等待状态,直到GC任务完成。GC优化很多时候就是减少stop-the-world 的发生。

回收哪些区域的对象

需要注意的是,JVM GC只回收堆内存方法区内的对象。而栈内存的数据,在超出作用域后会被JVM自动释放掉,所以其不在JVM GC的管理范围内。

堆内存常见参数配置

参数 描述
-Xms 堆内存初始大小,单位m、g
-Xmx 堆内存最大允许大小,一般不要大于物理内存的80%
-XX:PermSize 非堆内存初始大小,一般应用设置初始化200m,最大1024m就够了
-XX:MaxPermSize 非堆内存最大允许大小
-XX:NewSize(-Xns) 年轻代内存初始大小
-XX:MaxNewSize(-Xmn) 年轻代内存最大允许大小
-XX:SurvivorRatio=8 年轻代中Eden区与Survivor区的容量比例值,默认为8,即8:1
-Xss 堆栈内存大小
-XX:NewRatio=老年代/新生代 设置老年代和新生代的大小比例
-XX:+PrintGC jvm启动后,只要遇到GC就会打印日志
-XX:+PrintGCDetails 查看GC详细信息,包括各个区的情况
-XX:MaxDirectMemorySize 在NIO中可以直接访问「直接内存」,这个就是设置它的大小,不设置默认就是最大堆空间的值-Xmx
-XX:+DisableExplicitGC 关闭System.gc()
-XX:MaxTenuringThreshold 垃圾可以进入老年代的年龄
-Xnoclassgc 禁用垃圾回收
-XX:TLABWasteTargetPercent TLAB占eden区的百分比,默认是1%
-XX:+CollectGen0First FullGC时是否先YGC,默认false

TLAB 内存

TLAB全称是Thread Local Allocation Buffer即线程本地分配缓存,从名字上看是一个线程专用的内存分配区域,是为了加速对象分配而生的。

每一个线程都会产生一个TLAB,该线程独享的工作区域,java虚拟机使用这种TLAB区来避免多线程冲突问题,提高了对象分配的效率。

TLAB空间一般不会太大,当大对象无法在TLAB分配时,则会直接分配到堆上。

参数 描述
-Xx:+UseTLAB 使用TLAB
-XX:+TLABSize 设置TLAB大小
-XX:TLABRefillWasteFraction 设置维护进入TLAB空间的单个对象大小,他是一个比例值,默认为64,即如果对象大于整个空间的1/64,则在堆创建
-XX:+PrintTLAB 查看TLAB信息
-Xx:ResizeTLAB 自调整TLABRefillWasteFraction阀值。
7 种 JVM 垃圾回收器,你知道几个?

垃圾回收器总览

新生代可配置的回收器:Serial、ParNew、Parallel Scavenge

老年代配置的回收器:CMS、Serial Old、Parallel Old

新生代和老年代区域的回收器之间进行连线,说明他们之间可以搭配使用。

新生代垃圾回收器

Serial 垃圾回收器

Serial收集器是最基本的、发展历史最悠久的收集器。俗称为:串行回收器,采用复制算法进行垃圾回收

特点

串行回收器是指使用单线程进行垃圾回收的回收器。每次回收时,串行回收器只有一个工作线程。

对于并行能力较弱的单CPU计算机来说,串行回收器的专注性和独占性往往有更好的性能表现。

它存在Stop The World问题,及垃圾回收时,要停止程序的运行。

使用-XX:+UseSerialGC参数可以设置新生代使用这个串行回收器

ParNew 垃圾回收器

ParNew其实就是Serial的多线程版本,除了使用多线程之外,其余参数和Serial一模一样。俗称:并行垃圾回收器,采用复制算法进行垃圾回收

特点

ParNew默认开启的线程数与CPU数量相同,在CPU核数很多的机器上,可以通过参数-XX:ParallelGCThreads来设置线程数。

它是目前新生代首选的垃圾回收器,因为除了ParNew之外,它是唯一一个能与老年代CMS配合工作的。

它同样存在Stop The World问题

使用-XX:+UseParNewGC参数可以设置新生代使用这个并行回收器

ParallelGC 回收器

ParallelGC使用复制算法回收垃圾,也是多线程的。

特点

就是非常关注系统的吞吐量,吞吐量=代码运行时间/(代码运行时间+垃圾收集时间)

-XX:MaxGCPauseMillis:设置最大垃圾收集停顿时间,可用把虚拟机在GC停顿的时间控制在MaxGCPauseMillis范围内,如果希望减少GC停顿时间可以将MaxGCPauseMillis设置的很小,但是会导致GC频繁,从而增加了GC的总时间降低吞吐量。所以需要根据实际情况设置该值。

-Xx:GCTimeRatio:设置吞吐量大小,它是一个0到100之间的整数,默认情况下他的取值是99,那么系统将花费不超过1/(1+n)的时间用于垃圾回收,也就是1/(1+99)=1%的时间。

另外还可以指定-XX:+UseAdaptiveSizePolicy打开自适应模式,在这种模式下,新生代的大小、eden、from/to的比例,以及晋升老年代的对象年龄参数会被自动调整,以达到在堆大小、吞吐量和停顿时间之间的平衡点。

使用-XX:+UseParallelGC参数可以设置新生代使用这个并行回收器

老年代垃圾回收器

SerialOld 垃圾回收器

SerialOld是Serial回收器的老年代回收器版本,它同样是一个单线程回收器。

用途

  • 一个是在JDK1.5及之前的版本中与Parallel Scavenge收集器搭配使用,

  • 另一个就是作为CMS收集器的后备预案,如果CMS出现Concurrent Mode Failure,则SerialOld将作为后备收集器。

使用算法:标记 - 整理算法

ParallelOldGC 回收器

老年代ParallelOldGC回收器也是一种多线程的回收器,和新生代的ParallelGC回收器一样,也是一种关注吞吐量的回收器,他使用了标记压缩算法进行实现。

-XX:+UseParallelOldGc进行设置老年代使用该回收器

-XX:+ParallelGCThreads也可以设置垃圾收集时的线程数量。

CMS 回收器

CMS全称为:Concurrent Mark Sweep意为并发标记清除,他使用的是标记清除法。主要关注系统停顿时间。

使用-XX:+UseConcMarkSweepGC进行设置老年代使用该回收器。

使用-XX:ConcGCThreads设置并发线程数量。

特点

CMS并不是独占的回收器,也就说CMS回收的过程中,应用程序仍然在不停的工作,又会有新的垃圾不断的产生,所以在使用CMS的过程中应该确保应用程序的内存足够可用。

CMS不会等到应用程序饱和的时候才去回收垃圾,而是在某一阀值的时候开始回收,回收阀值可用指定的参数进行配置:-XX:CMSInitiatingoccupancyFraction来指定,默认为68,也就是说当老年代的空间使用率达到68%的时候,会执行CMS回收。

如果内存使用率增长的很快,在CMS执行的过程中,已经出现了内存不足的情况,此时CMS回收就会失败,虚拟机将启动老年代串行回收器;SerialOldGC进行垃圾回收,这会导致应用程序中断,直到垃圾回收完成后才会正常工作。

这个过程GC的停顿时间可能较长,所以-XX:CMSInitiatingoccupancyFraction的设置要根据实际的情况。

标记清除法有个缺点就是存在内存碎片的问题,那么CMS有个参数设置-XX:+UseCMSCompactAtFullCollecion可以使CMS回收完成之后进行一次碎片整理

-XX:CMSFullGCsBeforeCompaction参数可以设置进行多少次CMS回收之后,对内存进行一次压缩

G1 回收器

最近热文:

1、

2、

3、

4、

5、

6、

7、

8、!

9、

10、

点击「」获取面试题大全~

以上是关于你不得不知道的JVM 垃圾回收的主要内容,如果未能解决你的问题,请参考以下文章

深入JVM垃圾回收机制,值得你收藏

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

JVM系列第8讲:JVM 垃圾回收机制

JVM的垃圾回收算法有哪些

03 JVM的垃圾回收机制

扒一扒JVM的垃圾回收机制,下次面试你准备好了吗