《深入理解Java虚拟机》读后总结

 

JVM的GC概述

GC即垃圾回收,是指jvm用于释放那些不再使用的对象所占用的内存。

在充分理解了垃圾收集算法和执行过程后,才能有效的优化它的性能。

有些垃圾收集专用于特殊的应用程序。比如,实时应用程序主要是为了避免垃圾收集中断,而大多数OLTP应用程序则注重整体效率。

垃圾收集的目的在于清除不再使用的对象。gc通过确定对象是否被活动对象引用来确定是否收集该对象。两种常用的方法是引用计数和对象引用遍历。
引用计数
引用计数存储对特定对象的所有引用数,也就是说,当应用程序创建引用以及引用超出范围时,jvm必须适当增减引用数。当某对象的引用数为0时,便可以进行垃圾收集。
对象引用遍历
早期的jvm使用引用计数,现在大多数jvm采用对象引用遍历。对象引用遍历从一组对象开始,沿着整个对象图上的每条链接,递归确定可到达(reachable)的对象。如果某对象不能从这些根对象的一个(至少一个)到达,则将它作为垃圾收集。在对象遍历阶段,gc必须记住哪些对象可以到达,以便删除不可到达的对象,这称为标记(marking)对象。

 

基本的回收算法:

空间维度:标记-清除、标记-压缩、标记-复制、增量回收、分代回收

时间维度:串行回收、并发回收、并行回收

 

标记-清除:

标记清除的算法最简单,主要是标记出来需要回收的对象,然后然后把这些对象在内存的信息清除,会产生大量内存碎片。

 

da74f583-dea0-3e33-8cc1-2aac1559f3d4.jpg

标记-压缩

有时也叫标记-清除-压缩收集器,这个算法是在标记-清除的算法之上进行剪切操作,将存活对象压缩在一起,减少内存碎片。由于压缩空间需要一定的时间,会影响垃圾收集的时间。

9a116f66-872c-31f2-9a84-1daab899569e.jpg

标记-复制

 

这个算法是把内存分配为两个空间,一个空间(A)用来负责装载正常的对象信息,另外一个内存空间(B)是垃圾回收用的。

每次把空间A中存活的对象全部复制到空间B里面,在一次性的把空间A删除。

这个算法在效率上比标记-清除-压缩高,但是需要两块空间,对内存要求比较大,内存的利用率比较低。适用于短生存期的对象,持续复制长生存期的对象则导致效率降低。

 

13ba8250-57ee-3f77-aaff-4296e2a735a2.jpg

 

增量回收

增量回收器:把堆分为多个域,每次对从一个域进行垃圾回收。这样只会早点一小部分程序暂停。

分代回收:

基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代,对不同生命周期的对象使用不同的算法进行回收。

 

串行回收:

用单线程处理所有垃圾回收工作,因为无需多线程交互,所以效率比较高。但是,也无法使用多处理器的优势,所以此收集器适合单处理器机器

bb1cbb28-2ebc-318a-bba6-5b64815738f8.jpg

 

并行回收:

用多线程处理所有垃圾回收工作,利用多核处理器的优势。对于空间不大的区域(如young generation),采用并行收集器停顿时间很短,回收效率高,适合高频率执行。但是如果线程数量过多,导致线程之间频繁调度,也会影响性能。一半并行收集的线程是处理器的个数。

 

7d7ec04f-dfd1-335d-8c05-e8fa6db0af23.jpg

并发回收:

并发收集器GC时GC线程和应用线程大部分时间是并发执行,只是在初始标记(initial mark)和二次标记(remark)时需要stop-the-world,这可以大大缩短停顿时间(pause time),所以适用于响应时间优先的应用,减少用户等待时间。由于GC是和应用线程并发执行,只有在多CPU场景下才能发挥其价值,在一个N个处理器的系统上,并发收集部分使用K/N个可用处理器进行回收,一般情况下1<=K<=N/4。在执行过程中还会产生新的垃圾floating garbage(浮动垃圾),如果等空间满了再开始GC,那这些新产生的垃圾就没地方放了(并发收集器一般需要20%的预留空间用于这些浮动垃圾),这时就会启动一次串行GC,等待时间将会很长,所以要在空间还未满时就要启动GC。mark和sweep操作会引起很多碎片,所以间隔一段时间需要整理整个空间,否则遇到大对象,没有连续空间也会启动一次串行GC。采用此收集器,收集频率不能大,否则会影响到cpu的利用率,进而影响吞吐量。

cf035cd6-4b58-3b5d-b845-eb06286c77e6.jpg

 

JVM的GC触发原理

JVM的GC主要是对堆内存的回收,

一般把新生代的GC称为minor GC ,把老年代的GC成为 full GC,所谓full gc会先出发一次minor gc,然后在进行老年代的GC。

首先想eden区申请分配空间,如果空间够,就直接进行分配,否则进行一次Minor GC。

minor GC 首先会对Eden区的对象进行标记,标记出来存活的对象。然后把存活的对象copy到From空间。

如果From空间足够,则回收eden区可回收的对象。

如果from内存空间不够,则把From空间存活的对象复制到To区,

如果TO区的内存空间也不够的话,则把To区存活的对象复制到老年代。

如果老年代空间也不够(或者达到触发老年年垃圾回收条件的话)则触发一次full GC。

简单方向就是Eden->From->To->Old,如下图所示:

a57b0cef-88ec-3fdc-b996-d0209a07c0b8.jpg

 

默认是不会对持久带(方法区)进行垃圾回收的,设置参数可回收:-XX:+CMSClassUnloadingEnabled

 

JVM支持的GC收集器

JVM采用的是分代回收,不同代有不同的垃圾收集器

如图所示:连线的是可以组合使用

28237f6c-5cfc-3125-9f3c-432e7399192d.jpg

 

各个收集器的细节我就不在这里COPY/PASTE了,SerialOld收集器在书的图中没有,是我后加上的,其实很少使用。

感兴趣的可以试读,试读地址,有前三章的内容。