垃圾回收

如何确定一个对象是垃圾
想进行垃圾回收,得先知道什么样的对象是垃圾
引用计数法
如果一个对象没有任何指针对他引用,他就是垃圾
缺点:如果存在A/B相互引用,会永远无法回收
可达性分析
铜鼓GC Root 的对象,开始向下寻找,看某个对象是否可达
能作为GC Root的:
类加载器
Thread
虚拟机栈的本地变量表
static 成员
常量引用
本地方法栈的变量
垃圾回收算法
标记清除
标记
扫描堆中所有的对象,找出内存中需要回收的对象,标记出来,非常耗时
清除
清除掉被标记的需要回收的对象,释放空间
缺点
标记和清除太耗时间
产生大量的不连续的内存碎片,导致分配大对象时,放不进去而触发另一次垃圾回收
复制算法
将内存华为两块相等的区域,每次只使用其中的一块,当这块内存块使用完了,将还存活的对象复制到另一半上,然后清除已经使用过的内存空间
缺点
利用率低
标记-整理算法
标记过程跟标记清除算法一样,但是后续步骤是让存活的对象都向一端移动,再清理,这样内存就连续了
分代收集算法
年轻代:复制算法(对象生命周期短,yong区复制效率比较高)
老年代:标记清除或者标记整理(对象存活时间长,复制没必要,不如标记再清理)
垃圾收集器
概括下几种类型
单线程+复制+暂停用户-------Serial
多线程+复制+暂停用户------Parnew
平行+复制------ paraller Scanvenge
单线程+标记整理+暂停用户----serial Old
平行+标记整理----paraller Old
Serial 收集器
是一种单线程收集器,使用一个收集线程去完成垃圾收集,并且在垃圾收集的时候需要暂停其他线程
优点:简单高效
缺点:收集过程需要暂停所有线程
算法:复制算法
适合范围:新生代
应用:client模式下的默认新生代收集器
ParNew 收集器
可以理解为Serial收集器的多线程版本
优点:多CPU时,效率比Serial高
缺点:收集过程中还是要暂停所有的应用程序线程,单CPU时还比Serial差
算法:复制算法
适用范围:新生代
应用:运行在Server模式下的虚拟机中首选的新生代收集器
Paraller(平行的) Scavenge(捡垃圾) 收集器
paraller 是一个新生代收集器,也是使用的复制算法的收集器,又是并行的多线程收集器,看上去和ParNew一样,但是Paraller更关注系统的吞吐量
吞吐量=运行用户代码的时间/(运行用户代码的时间+垃圾收集时间)
比如虚拟机总共运行了100分钟,垃圾收集时间用了1分钟,吞吐量=(100-1)/100=99%。
吞吐量越大,意味着垃圾收集的时间越短,用户代码可以充分利用CPU资源
-XX:MaxGCPauseMillis控制最大的垃圾收集停顿时间,
-XX:GCTimeRatio直接设置吞吐量的大小
Serial Old 收集器
Serial Old 收集器是Serial收集器的老年代版本,也是一个单线程收集器,不过用的是标记-整理算法,运行过程和Serial收集器一样
Paraller Old 收集器
paraller Old收集器是 Paraller Scavenge 收集器的老年代版本,使用 标记-整理算法进行垃圾回收
吞吐量优先
CMS 收集器
Concurrent Mark Sweep
是老年代的垃圾收集器
是并发的【用户线程和垃圾会输线程可以同时执行】
标记-清除算法
追求最短回收停顿时间
适合互联网站和B/S系统的服务端
步骤
1、初始标记 标记GC Root能关联到的对象 stw
2、并发标记 GC Roots Tracing
3、重新标记 修改并发标记因用户程序变动的内容 stw
4、并发清除
优点:并发收集、停顿低
缺点:产生大量的空间碎片、并发阶段降低吞吐量
G1 收集器
面向服务端的收集器
特点:
并发与并行
分代收集
标记-整理算法
可预测的停顿(可以设置垃圾收集消耗的时间不能超过N毫秒)
原理
G1将整个java堆划分为多个大小相等独立的区域,使得新生代和老年代不再物理隔离
步骤:
1、初始标记
标记GC Roots能关联到的对象,修改TAMS值,stw
2、并发标记
从GC Roots进行可达性分析,找出存活对象,与用户并发执行
3、最终标记
修正在并发标记阶段因为用户程序的并发执行导致变动的数据,stw
4、筛选回收
对各个区域的回收价值和成本进行排序,根据用户所期望的GC停顿时间制定回收计划
垃圾收集器分类
串行收集器
Serial 和Serial Old
只能有一个垃圾回收线程,用户线程暂停
并行收集器[吞吐量优先]
Paraller Scanvege、Paraller Old
多条垃圾收集器并行干工作,但是用户线程仍然需要处于等待状态
并发收集器【停段时间优先】
CMS、G1
用户线程和垃圾收集器同时执行,垃圾收集器在执行的时候不会停顿用户线程
吞吐量和停顿时间
停顿时间:垃圾收集器进行垃圾回收终端应用执行响应的时间
吞吐量:运行用户代码时间/(运行用户代码时间+垃圾收集时间)
停顿时间越短就越适合需要和用户交互的程序,良好的响应速度提升用户体验
高吞吐量可以高效的利用CPU时间,尽快完成程序的运算任务,主要适合后台运算不需要太多交互的任务
如何选择合适的垃圾收集器
优先调整堆的大小让服务器自己来选择
如果内存小于100M,使用串行收集器
如果是单核,并且没有停顿时间要求,使用串行或JVM自己选
如果允许停顿时间超过1秒,选择并行或JVM自己选
如果响应时间最重要,并且不能超过1秒,使用并发收集器
对于G1收集
再次理解G1
判断是否需要使用G1收集器
(1)50%以上的堆被存活对象占用
(2)对象分配和晋升的速度变化非常大
(3)垃圾回收时间比较长
如何开启需要的垃圾收集器
(1)串行
-XX:+UseSerialGC
-XX:+UseSerialOldGC
(2)并行(吞吐量优先):
-XX:+UseParallelGC
-XX:+UseParallelOldGC
(3)并发收集器(响应时间优先)
-XX:+UseConcMarkSweepGC
-XX:+UseG1GC