一、垃圾回收解决3个问题
1、哪些内存需要被回收?
- 程序计数器、虚拟机栈、本地方法栈这3个区域随线程而生,随线程而灭,方法结束或线程结束时,内存自然就跟随回收了,因此无须过多考虑回收问题。
- 堆和方法区内存是动态分配的,垃圾收集器所关注的是这部分内存。但是方法区回收性价比低,虚拟机规范不要求对其进行回收,因此,垃圾收集器最终只需关注java堆的回收。
2、什么时候回收?
判断对象已死则回收,两种对象存活判定方法:
- 引用计数算法:给对象添加一个引用计数器,每增加一个引用则计数值加1;当引用失效则计数器减1;当引用计数器为0,则对象不可用。该方法无法解决循环引用问题。
- 可达性分析算法:GC Roots对象作为起点,向下搜索,搜索路径称为引用链,当一个对象到GC Roots没有任何引用链相连,则此对象不可用。
GC Roots对象:
A、虚拟机栈和本地方法栈引用的对象。
B、方法区常量或类静态属性所引用的对象。
3、如何回收?
垃圾收集器
image.png1、新生代
-
Serial
单线程收集器,使用复制算法,在垃圾收集期间,必须暂停其他所有工作线程,直到它收集结束。虚拟机运行在client模式下默认的新生代收集器。具有简单高效的优点。
image.png -
ParNew
Serial收集器的多线程版本,支持线程并行去完成垃圾收集工作,使用复制算法,在垃圾收集期间,必须暂停其他所有工作线程,直到它收集结束。虚拟机运行在server模式下默认的新生代收集器。性能与Serial类似。
ParNew收集器也是使用-XX:+UseConcMarkSweepGC选项后默认的新生代收集器,也可以使用-XX:+UseParNewGC选项来强制指定。
ParNew默认开启的线程数与CPU数量相同,可以使用-XX:ParallelGCThreads来限制垃圾收集器的线程数。
image.png -
Parallel Scavenge
并行多线程收集器,使用复制算法,区别于其他收集器的关注点:尽可能缩短垃圾收集时用户线程停顿的时间,Parallel Scavenge目标则是达到一个可控制的吞吐量。所谓吞吐量就是CPU用于运行用户代码时间与CPU总消耗时间的比值,吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。参考ParNew配图。
两个参数控制吞吐量:
A、-XX:MaxGCPauseMillis 最大垃圾收集停顿时间
B、-XX:GCTimeRatio 吞吐量大小,垃圾收集时间占总时间比率,相当于吞吐量的倒数。计算1/(1+n)。
C、-XX:UseAdaptiveSizePolicy 自适应调节策略开关,把内存调优任务交给虚拟机去完成。
2、老年代
-
Serial Old(MSC)
单线程收集器。Serial收集器的老年代版本,使用标记-整理算法。主要给client模式下的虚拟机使用。
image.png -
Parallel Old
多线程收集器。Parallel Scavenge收集器的老年代版本,使用标记-整理算法。注重吞吐量以及CPU资源敏感的场合,优先考虑Parallel Scavenge加Parallel Old收集器。
image.png -
CMS
CMS目标是获取最短回收停顿时间。使用标记-清除算法。整个过程4个步骤:
A、初始标记【stop the world】
B、并发标记
C、重新标记【stop the world】
D、并发清除
image.png
3个缺点:
A、CPU资源敏感:启动线程数=(CPU数量+3)/4,当CPU不足4个时,对用户程序影响较大。
B、无法处理浮动垃圾:并发清理阶段产生的垃圾,使用参数-XX:CMSSInitiatingOccupancyFraction设置触发GC执行的老年代内存使用百分比。该参数设置过高易出行Concurrent Mode Failure异常(剩余内存不足以运行GC程序)。
C、因使用标记-清除算法导致的空间碎片问题,开关参数-XX:+UseCMSCopactAtFullCollection是用在CMS收集器要进行FullGC时开启内存碎片的合并整理过程,参数-XX:CMSFullGCsBeforeComoaction用于设置执行多少次不压缩的FullGC后,跟着来一次带压缩的。
3、横跨新老年代
-
G1
G1目标是降低回收停顿时间,使用标记-整理算法。
将java堆划分为多个大小相等的独立区域Region,G1跟踪各个Region垃圾堆积的价值大小(回收所获得的空间大小与回收所需时间的比值),维护一个优先列表,优先回收价值最大的Region,这种Region划分内存空间以及按优先级的区域回收方式,使得G1在有限的时间内获得尽可能高的收集效率。
G1收集器分为4个过程:
初始标记【stop the world】
并发标记
最终标记【stop the world】
筛选回收
image.png
CMS与G1的区别:
A、G1横跨新老年代,使用标记-整理算法,不会产生空间碎片;CMS是老年代收集器,使用标记-清除算法,会产生空间碎片。
B、降低垃圾收集停顿时间是两者共同关注点,但是G1建立了可预测的停顿时间模型。
C、G1将整个java堆划分为大小一致的region,跟踪各个region里面垃圾堆积的价值大小,维护一个优先列表,优先回收价值最大的region。
垃圾收集算法
-
标记-清除算法
算法分为标记-清楚两个阶段:首先标记出所有需要回收的对象,然后统一回收所有被标记的对象。
2个不足:
A、 一个是效率低。两个过程效率都不高
B、另一个是空间问题,标记清除之后会产生大量不连续的内存碎片,如果程序在运行过程中需要为较大对象分配内存时,可能找不到足够连续的内存而不得不提前触发另一次垃圾收集动作。
image.png -
复制算法
将内存分为大小相等两块,每次只使用其中一块。当这一块内存用完,就将还存活的对象复制到另外一块上面,再把已使用过的内存空间一次清理掉。
解决1个问题:
A、解决了标记-清除算法效率低的问题。
image.png -
标记-整理算法
算法分为标记-整理两个阶段:首先标记出所有需要回收的对象,让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
解决2个问题:
A、解决标记-清除算法空间不连续的问题。
B、解决复制算法浪费50%内存的问题。
image.png -
分代收集算法
根据对象存活周期不同将java堆划分为新生代和老年代。新生代中,对象存活率低,因此选用复制算法。在老年代中,对象存活率高,选用标记-清除或标记-整理算法。
二、内存分配
- 新生代包括一个Eden区、两个Survivor区,内存比例为8:1。对象优先在Eden分配,如果设置了-XX:PretenureSizeThreshold参数,则令大于这个设置值的对象直接在老年代分配。如果Eden区内存不足以分配当前对象,则触发Minor GC,如果对象在Eden出生并经过一次Minor GC后仍然存活,如果能被Survivor容纳【否则通过担保机制移入老年代】,将被移动到Survivor空间,对象年龄加1,对象在Survivor每熬过一次Minor GC,年龄就加1岁,当年龄增加到老年代年龄阈值-XX:MaxTenuringThreshold,将被移入老年代。
网友评论