转载、引用请标明出处
https://www.jianshu.com/p/8ec0ee8ca7ee
本文出自zhh_happig的简书博客,谢谢
以下内容,是本人学习的笔记和工作中的总结,仅供大家参考,有误的地方还请指正
一 判定垃圾对象算法
-
引用计数法
- 在对象中添加一个计数器,记录着指向对象的引用的个数
- 当有新的引用指向这个对象的时,计数器+1,当指向对象的引用被置null时,计数器-1
- 当计数器的值为0,那么这个对象就是垃圾对象,就会被垃圾回收器回收
- 缺陷:当对象1中有引用指向对象2,对象2中有引用指向对象1,但是再也没有其他外部引用指向对象1和对象2,对象1、对象2是垃圾对象。通过引用计数法,对象1、对象2计数器都为1,不会被垃圾回收器回收。
- 不推荐使用
-
可达性分析法
- 该算法的基本思路就是通过GC Roots对象作为起点,从这些节点开始向下搜索
- 搜索走过的路径被称为引用链
- 当一个对象,从GC Roots节点向下搜索,没有任何一条路径能够达到该对象,那么这个对象就是垃圾对象
- 可以做为GC Roots的对象包括
- 虚拟机栈中引用指向的对象
- 方法区中类静态属性所引用的对象
- 方法区中常量所引用的对象
- 本地方法栈中引用的对象
二 垃圾回收算法
-
标记-清除算法
- 分为标记和清楚2步
- 标记:就是通过可达性分析法将对象标记为垃圾对象
- 清除:将垃圾对象回收
- 缺点:存在效率问题和空间问题
- 垃圾对象在堆内存中是散乱的,被清除之后,会导致越来越多不连续的空间,当分配大对象时,要寻找一片合适的内存空间,就会变得困难
- 如果找不到合适的内存空间存放这个大对象,就会触发一次GC,影响性能
-
复制算法
- 年轻代的垃圾收集算法,在年轻代对象存活率低,复制算法效率很高
- 将堆内存分成2部分,只在其中一部分中分配内存
- 垃圾回收后,把存活的对象复制到另一部分内存连续的空间上
- 就这样在2个内存中循环复制,这样解决了 标记-清除算法 中的效率问题
- 缺点:由于只在其中一部分中分配内存,存在内存浪费问题
- 再将内存细分,即可降低内存浪费,详见Eden、Survivor 0 、Survivor 1、Tenured
-
标记-整理算法
- 年老代的垃圾收集算法
- 在年老代对象存活率高,复制算法效率低,使用标记-整理算法
- 标记过程仍然与“标记-清除”算法一样,
- 整理:不是直接清除,而是对内存里面的对象进行重新整理,让所有存活的对象都向一端移动,依次排列,那么端边界另一边的就都是垃圾对象了,直接清理掉
- 标记-整理算法唯一的缺点就是效率也不高,不仅要标记所有存活对象,还要整理所有存活对象的引用地址。从效率上来说,标记-整理算法要低于复制算法。
-
分代收集算法
- 根据不同的内存区选择不同的算法
- 在年轻代,选择复制算法
- 在年老代,选择标记-整理算法
三 垃圾回收器
-
Serial
- 年轻代垃圾收集器,采用复制算法
- 串行收集器:如果serial要收集垃圾,必须让程序其他线程停掉,收集完毕,程序其他线程继续执行
- 单线程垃圾收集器:serial单线程收集垃圾
- 整体效率低
-
Serial Old
- 它是Serial收集器的年老代版本,与Serial相似,区别是使用“标记-整理”算法
-
Parnew
- 年轻代垃圾收集器,采用复制算法
- 串行收集器:如果parnew要收集垃圾,必须让程序其他线程停掉,收集完毕,程序其他线程继续执行
- 多线程垃圾收集器:parnew多线程收集垃圾
-
Parallel scavenge
- 与parnew类似,区别是parallel scavenge能控制吞吐量
- 吞吐量 = 执行用户代码的时间 / (执行用户代码的时间 + 垃圾回收所占用的时间)
- -xx:MaxGCPauseMillis 垃圾收集器停顿最大时间,就是垃圾收集线程(多线程)收集垃圾的时间,是不是设置越小就越好呢?假设设置100ms,垃圾收集器分配的内存可能是1G,设置为1ms,垃圾收集器分配的内存可能只有10M,因为只有10M这么小的内存才能在1ms内收集完,那么10M内存显然太小了,就会造成垃圾频繁回收。原来100ms回收一次的任务,现在如果1ms回收一次,可能要频繁回收80次,对比之下性能并没有多大提升。所以不是设置的越小越好,应该更具实际情况设置一个合理值。
- -xx:CGTimeRatio 吞吐量大小
- 适用注重吞吐量的后端服务
-
Parallel Old
- 是Parallel Scavenge收集器的年老代版本,与Parallel Scavenge相似,但与Parallel Scavenge不同的是,它使用的是“标记-整理算法”
- 使用方式:-XX:+UseParallelOldGC,打开该收集器后,将使用Parallel Scavenge(年轻代)+Parallel Old(年老代)的组合进行GC
-
CMS
- 年老代、永久代垃圾收集器,采用标记-清除算法
- 并行收集器:垃圾收集线程可以和程序其他线程并发执行
- 多线程垃圾收集器:CMS多线程收集垃圾
-
工作过程
- 初始标记:造成程序其他线程停顿。标记垃圾对象
- 并发标记:与程序线程并发运行。在运行期间会发生对象的引用变更等等情况,对于这些对象,都是需要进行重新标记的,否则会发生漏标的情况。这个阶段因为是并发的容易导致concurrent mode failure
- 重新标记:会造成程序其他线程停顿。重新标记的内存范围是整个堆,包含young_gen和old_gen。为什么要扫描新生代呢,因为对于老年代中的对象,如果被新生代中的对象引用,那么就会被视为存活对象,即使新生代的对象已经不可达了。所以需要标记young_gen和old_gen。
- 并发清理:与程序线程并发运行。并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就称为“浮动垃圾”。
- 并发重置:与程序线程并发运行。重新设置CMS算法内部的数据结构,准备下一个CMS生命周期的使用
- 优点
- 并发收集
- 低停顿:只有初始标记和重新标记会造成程序其他线程短暂停顿
- 缺点
- 占用大量的cpu资源
- 无法处理浮动垃圾
- 出现Concurrent Mode Failure
- CMS收集与应用线程会同时增加对堆内存的占用,也就是说,CMS必须要在老年代堆内存用尽之前完成垃圾回收,否则CMS回收失败,出现Concurrent Mode Failure;
- 年老代的剩余空间无法满足新对象的空间分配,也会出现这个错误
- 在出现Concurrent Mode Failure时,将触发担保机制,Serial Old收集器将会以STW的方式进行一次full gc,从而造成较大停顿时间,影响性能
-
G1(Garbage First)
- JDK7增加,成为HotSpot重点发展的垃圾回收技术,被HotSpot团队寄予取代CMS的使命
- 将会被安排成为JDK9的默认垃圾收集器
- 并行收集器:垃圾收集线程可以和程序其他线程并发执行
- 多线程垃圾收集器:G1多线程收集垃圾
- 详见G1垃圾收集器
四 垃圾回收器作用内存区域以及他们之间的组合关系
-
7种不同分代的收集器
- Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1;
-
垃圾收集器所属区域
- 年轻代收集器:Serial、ParNew、Parallel Scavenge
- 年老代收集器:Serial Old、Parallel Old、CMS
- 整堆收集器:G1
-
两个收集器间有连线,表明它们可以搭配使用
- Serial/Serial Old
- Serial/CMS
- ParNew/Serial Old
- ParNew/CMS
- Parallel Scavenge/Serial Old
- Parallel Scavenge/Parallel Old
- G1
以上内容,是本人学习的笔记和工作中的总结,仅供大家参考,有误的地方还请指正
转载、引用请标明出处
https://www.jianshu.com/p/8ec0ee8ca7ee
本文出自zhh_happig的简书博客,谢谢