JAVA学习笔记--垃圾收集器

JAVA学习笔记--垃圾收集器

一、概述

参考:http://blog.csdn.net/ffm83/article/details/42873001

JAVA学习笔记--垃圾收集器_第1张图片
javaGC
参数 描述 新生代GC方式 老年代和持久代GC方式
-XX:+UseSerialGC Jvm运行在Client模式下的默认值,打开此开关后,使用Serial + Serial Old的收集器组合进行内存回收 Serial 串行GC Serial Old 串行GC
-XX:+UseParNewGC 打开此开关后,使用ParNew + Serial Old的收集器进行垃圾回收 ParNew 并行GC Serial Old 串行GC
-XX:+UseConcMarkSweepGC 使用ParNew + CMS + Serial Old的收集器组合进行内存回收,Serial Old作为CMS出现“Concurrent Mode Failure”失败后的后备收集器使用。 ParNew 并行GC CMS 并发GC 当出现“Concurrent Mode Failure”时采用Serial Old 串行GC
-XX:+UseParallelGC Jvm运行在Server模式下的默认值,打开此开关后,使用Parallel Scavenge + Serial Old的收集器组合进行回收 Parallel Scavenge 并行回收GC Serial Old 串行GC
-XX:+UseParallelOldGC 使用Parallel Scavenge + Parallel Old的收集器组合进行回收 Parallel Scavenge 并行回收GC Parallel Old 并行GC

二、对象存活判断

  1. 引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。
  2. 可达性分析(Reachability Analysis):从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,不可达对象。

在Java语言中,GC Roots包括:

  1. 虚拟机栈中引用的对象。
  2. 方法区中类静态属性实体引用的对象。
  3. 方法区中常量引用的对象。
  4. 本地方法栈中JNI引用的对象。

三、垃圾收集算法

1. 标记-清除算法

“标记-清除”(Mark-Sweep)算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其缺点进行改进而得到的。

主要缺点有两个:一个是效率问题,标记和清除过程的效率都不高;另外一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。


JAVA学习笔记--垃圾收集器_第2张图片

2. 复制算法

“复制”(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为原来的一半,持续复制长生存期的对象则导致效率降低。


JAVA学习笔记--垃圾收集器_第3张图片

3. 标记-压缩算法

复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。

根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存


JAVA学习笔记--垃圾收集器_第4张图片

4. 分代收集算法

GC分代的基本假设:绝大部分对象的生命周期都非常短暂,存活时间短。

“分代收集”(Generational Collection)算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。

四、垃圾收集器

1. Serial收集器(串行GC)[新生代收集器]

执行gc时,会暂停jvm其它的工作线程(Stop The World)

标记eden和servivor(form区)的活动对象,标记结束后,将eden和servivor(form)区的所有活动对象转到servivor(to)区,这时一些大对象会被直接复制到old区,然后eden和servivor(form)区里剩余的对象均是垃圾了,释放他们占用的内存。这时将servivor 的to区标记为form,form标志为to区。

如果在复制到to区时,to区已满,则直接复制到old区。

  1. 新生代采用复制算法
  2. 启用串行收集器: -XX:+UseSerialGC
  3. Client模式下虚拟机的默认收集器[简单高效]
JAVA学习笔记--垃圾收集器_第5张图片
Seria

2. ParNew收集器(并行GC) [新生代收集器]

Serial收集器的多线程版
并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态

  1. 新生代采用复制算法,暂停所有用户线程
  2. 老年代采用标记整理算法,在暂停所有用户线程
  3. 可以和CMS收集器配合工作
  4. 启用方式: -XX:_UseParNewGC
JAVA学习笔记--垃圾收集器_第6张图片
ParNe

3. Parallel Scavenge收集器(并行GC) [新生代收集器]

  • 吞吐量优先收集器
  • 使用复制算法的收集器
  • 并行的多线程收集器
  • 与其他收集器关注点不一样,Parllel Scavenge收集器的目标是达到一个可控制的吞吐量,其他的收集器尽可能的缩短垃圾收集时用户线程的卡顿时间。

吞吐量:就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)

  1. 参数[-XX:MaxGCPauseMillis]:控制最大垃圾收集时间(大于零的毫秒数)。不要认为把这个值设置的越小越好,GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的;系统吧新生代调小一些,每次收集停顿时间肯定小了,但直接导致垃圾收集更频繁。(10秒一次,每次停顿100毫秒 VS 5秒一次,每次停顿70毫秒)
  2. 参数[-XX:GCTimeRatio]:直接设置吞吐量大小(大于零小于100整数)【如果值设置19,那允许GC时间占比5%[1/(1+19)]】。
  3. 参数[-XX:UseAdaptiveSizePolicy]:这个参数打开后,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整[新生代大小(-Xmn);Eden和Servivor区域占比(-XX:ServivorRatio),晋升老年代对象年龄(-XX:PretenureSizeThreshold)]这些参数以提供最合适的停顿时间或者吞吐量。

4. Serial Old收集器(串行GC)[老年代收集器]

  • 是Serial收集器的老年代版本,单线程收集器
  • 采用“标记-整理”算法实现
  • 这个收集器主要意义给Client模式下的虚拟机使用
  • 在server模式可以下,可以与Parallel Scavenge收集器搭配使用,另一种是作为CMS收集器的后背预案。


    JAVA学习笔记--垃圾收集器_第7张图片

5. Parallel Old收集器(并行GC)[老年代收集器]

  • 使用多线程收集
  • 采用“标记-整理”算法实现

在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器
Parallel Scavenge收集器无法与CMS收集器组合,Parallel Old收集器出现之前,只能是Parallel Scavenge加Serial Old组合。

JAVA学习笔记--垃圾收集器_第8张图片

6. CMS收集器(Concurrent Mark Sweep)[老年代收集器]

  • 以获取最短回收停顿时间为目标的收集器
  • 基于"标记-清除"算法实现
  • 并发收集,低停顿

缺点
1. 对CPU资源敏感,CMS默认启动回收线程数(CPU数量+3)/4,保证在CPU数量大于4个的时候不低于CPU数量的25%。
2. CMS无法处理浮动垃圾(Floating GarBage),可能出现“Current Mode Failure”失败而导致另一次Full GC产生。也由于垃圾收集阶段用户线程还在运行,还需要预留足够内存空间给用户线程使用(JDK1.5默认68%;JDK1.6默认92%【-XX:CMSInitiatingOccupancyrFraction】);要是CMS运行时预留的内存无法满足用户线程需要,就会出现一次“Current Mode failure",虚拟机启动后备预案:使用Serial Old重新收集老年代垃圾收集【这样停顿时间就很长了】。
3. CMS采用”标记-清除“算法,会产生大量的空间碎片。老年代有足够的剩余空间,但是无法找打足够大内存分配对象,提前出发一次Full GC。CMS提供了【-XX:+UseCompactAtFullCollection,默认值0:每次都整理】参数开启内存碎片整理。

四个步骤:

  1. 初始标记(Stop the world): 标记下GC ROOTS能直接关联到的对象
  2. 并发标记: 进行GC RootsTrancing的过程
  3. 重新标记(Stop the world): 修正并发标记期间因用户线程继续运作而导致标记产生变动的那异步部分对象的标记记录
  4. 并发清除


    JAVA学习笔记--垃圾收集器_第9张图片

7. G1收集器(Garbage First)

  • 面向服务端的收集器
  • 并行与并发收集器:充分利用多CPU、多核环境下的硬件优势,使用多个CPU(CPU或CPU核心)缩短Stop The World 时间。
  • 分代收集:将整个堆划分为多个大小相等的区域(Region),跟踪每个Region的垃圾堆积的价值大小,在后台维护一个优先列表,每次优先收集价值最大的Region,保证GC有限时间内获得最大的收集效率。【Region之间的对象应用虚拟机采用Remembered Set来避免全堆扫描】
  • 空间整合:G1整体上看是基于“标记-整理”算法实现,从局部(两个Region之间)上来看是基于“标记-复制"算法实现的。
  • 可预测的停顿:能让使用者明确指定在一个长度为M毫秒的时间段内,消耗在垃圾收集器上的时间不超过N毫秒。

大致分为以下几个步骤:

  1. 初始标记(Initial Marking)
  2. 并发标记(Concurrent Marking)
  3. 最终标记(Final Marking)
  4. 筛选回收(Live Data Counting and Evacuation)
JAVA学习笔记--垃圾收集器_第10张图片

五、部分JVM参数

参数 说明
-XX:SurvivorRatio 新生代中Eden区域与Survivor区域的容量比值,默认为8,代表Eden:Subrvivor = 8:1
-XX:PretenureSizeThreshold 直接晋升到老年代对象的大小,设置这个参数后,大于这个参数的对象将直接在老年代分配
-XX:MaxTenuringThreshold 晋升到老年代的对象年龄,每次Minor GC之后,年龄就加1,当超过这个参数的值时进入老年代
-XX:UseAdaptiveSizePolicy 动态调整java堆中各个区域的大小以及进入老年代的年龄
-XX:+HandlePromotionFailure 是否允许新生代收集担保,进行一次minor gc后, 另一块Survivor空间不足时,将直接会在老年代中保留
-XX:ParallelGCThreads 设置并行GC进行内存回收的线程数
-XX:GCTimeRatio GC时间占总时间的比列,默认值为99,即允许1%的GC时间,仅在使用Parallel Scavenge 收集器时有效
-XX:MaxGCPauseMillis 设置GC的最大停顿时间,在Parallel Scavenge 收集器下有效
-XX:CMSInitiatingOccupancyFraction 设置CMS收集器在老年代空间被使用多少后出发垃圾收集,默认值为68%,仅在CMS收集器时有效,-XX:CMSInitiatingOccupancyFraction=70
-XX:+UseCMSCompactAtFullCollection 由于CMS收集器会产生碎片,此参数设置在垃圾收集器后是否需要一次内存碎片整理过程,仅在CMS收集器时有效
-XX:+CMSFullGCBeforeCompaction 设置CMS收集器在进行若干次垃圾收集后再进行一次内存碎片整理过程,通常与UseCMSCompactAtFullCollection参数一起使用
-XX:+UseFastAccessorMethods 原始类型优化
-XX:+DisableExplicitGC 是否关闭手动System.gc
-XX:+CMSParallelRemarkEnabled 降低标记停顿
-XX:LargePageSizeInBytes 内存页的大小不可设置过大,会影响Perm的大小,-XX:LargePageSizeInBytes=128m

你可能感兴趣的:(JAVA学习笔记--垃圾收集器)