Java虚拟机(二):垃圾收集器
2016-06-11 16:19
330 查看
程序计数器、虚拟机栈、本地方法栈3个区域随线程而生,随线程而灭,在这几个区域内就不需要过多考虑垃圾回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。 栈中的栈帧随着方法的进入和退出而有条不紊的执行这出栈和入栈操作。
因此虚拟机主要关注java堆和方法区的内存分配和回收。
1、对象存活判定
1)引用计数算法
给对象添加一个引用计数器,每当由一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
缺点:很难解决对象相互循环引用的问题(对象相互循环引用,但其实他们都已经没有用了)。
2)可达性分析算法
通过一系列的成为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GC ROOTS没有任何引用链相连时,则证明此对象时不可用的。
Java语言中GC Roots的对象包括下面几种:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈JNI(Native方法)引用的对象
2、垃圾收集算法
1)标记-清除算法
算法分为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
不足:一个是效率问题,标记和清除两个过程的效率都不高;另一个是空间问题,标记清楚之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后再程序运行过程中需要分配较大的对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
2)复制算法
将可用内存按照容量划分为大小相等的两块,每次只使用其中的一块。当这块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可。
不足:将内存缩小为了原来的一半。
实际中我们并不需要按照1:1比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor
当另一个Survivor空间没有足够空间存放上一次新生代收集下来的存活对象时,这些对象将直接通过分配担保机制进入老年代。
3)标记-整理算法
类似于标记-清除,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存。
3、Java虚拟机垃圾收集器
1)Java内存分带划分
Java虚拟机根据对象存活的生命周期长短,将java堆划分为新生代、老年代。而方法区主要是持久代,也参与垃圾回收。
参数设置:
-Xms128M :设置堆最最小128M
-Xmx512M :设置堆最大512M
-Xmn20M :设置新生代20M
-XX:SurvivorRatio=8 :设置Eden区和Survivor区的大小(8:1),分别为16M、2M
-XX:PermSize=64M:设置持久代大小64M
-XX:MaxPermSize=128M:设置持久代最大128M
2)垃圾收集器
可以看到,新生代收集器有Serial、ParNew、Parallel Scanenge收集器,老年代收集器有CMC、Serial Old、Parallel Old收集器。G1收集器是一个全带收集器,代表当前收集器技术发展的前沿成果。
Serial收集器:
采用复制算法 ,新生代收集器。
这个收集器是一个单线程的收集器,但它的单线程的意义不仅仅说明它会只使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。
ParNew 收集器:
采用复制算法 ,新生代收集器。
ParNew收集器是Serial收集器的多线程版本,除了使用了多线程进行收集之外,其余行为和Serial收集器一样。
并行:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
并发:指用户线程与垃圾收集线程同时执行(不一定是并行的,可能会交替执行),用户程序在继续执行,而垃圾收集程序运行于另一个CPU上。
Parallel Scavenge 收集器:
采用复制算法 ,新生代收集器。
Parallel Scavenge收集器是一个新生代收集器,它是使用复制算法的收集器,又是并行的多线程收集器。该收集器的目标是达到一个可控制的吞吐量,而
非其他收集器的尽可能缩短垃圾收集时用户线程的等待时间。
吞吐量:就是CPU用于运行用户代码的时间与CPU总消耗时间的比值。即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。
参数 -XX:MaxGCPauseMillis参数用于控制最大垃圾收集停顿时间。
参数 -XX:GCTimeRatio直接设置吞吐量大小(垃圾收集时间比率,吞吐量的倒数)。
Serial Old 收集器:
采用标记整理的算法,老年代收集器。
Serial Old 是Serial收集器的老年代版本,是一个单线程收集器。
Parallel Old 收集器:
采用标记整理的算法,老年代收集器。
Parallel Old收集器是Paraller Scavenge收集器的老年代版本,使用多线程和标记整理算法。
CMS收集器:
采用标记清除算法,老年代收集器。
CMS收集器是基于标记清除算法实现的,整个过程分为4个步骤:
1.初始标记2.并发标记3.重新标记4.并发清除。其中,初始标记和重新标记这两步骤仍然需要“Stop the world”。
优点:并发收集、低停顿。
缺点:1.CMS收集器对CPU资源非常敏感,CMS默认启动的回收线程数是(CPU数量+3)/4。
2.CMS收集器无法处理浮动垃圾,可能出现Failure失败而导致一次Full Gc地产生,得搭配Serial Old使用。
3.CMS是基于标记清除算法实现的,存在碎片问题。
(3)收集器搭配使用及参数设置
在当前jdk中,主要分为server模式和client模式,默认是client模式,而在不同的模式中默认的垃圾收集算法也是不一样的。在client模式中,新生代和老年代默认是Serial和Serial Old,在server模式中默认的新生代和老年代收集器是Parallel
Scavenge+Serial Old。java虚拟机的-XX:+PrintGCDetails参数可以打印垃圾收集器的日志信息。
使用串行收集器
参数: -XX:+UseSerialGC
说明:虚拟机运行在Client模式的默认值,打开此开关参数后,使用Serial+Serial
Old收集器组合进行垃圾收集。
使用ParNew并行收集器
参数:-XX:+UseParNewGC
说明:打开此开关参数后,使用ParNew+Serial Old收集器组合进行垃圾收集。
使用并行收集器
参数:-XX:+UseParallelGC
-XX:+UseAdaptiveSizePolicy
:java虚拟机动态自适应策略,动态调整年老代对象年龄和各个区域大小。
-XX: ParallelGCThreads:设置并行GC时进行内存回收的线程数。
说明:打开此开关参数后,使用Parallel
Scavenge+Serial Old收集器组合进行垃圾收集。
使用并行Old收集器
参数:-XX:+UseParallelOldGC
-XX:+UseAdaptiveSizePolicy
:java虚拟机动态自适应策略,动态调整年老代对象年龄和各个区域大小。
-XX: ParallelGCThreads:设置并行GC时进行内存回收的线程数。
说明:打开此开关参数后,使用Parallel
Scavenge+Parallel Old收集器组合进行垃圾收集。
使用并发收集器
参数:-XX:+UseConcMarkSweepGC
-XX:+CMSInitiatingOccupancyFraction
默认68%,控制当老年代空间使用率超过该值,启动垃圾收集。
-XX:+UseCMSCompactAtFullCollection 默认开启,设置CMS收集器在完成垃圾收集之后是否进行一次内存整理。
-XX:+CMSFullGCsBeforeCompaction 默认值0,设置CMS收集器在进行多少次垃圾收集不压缩整理之后才进行一次内存整理。
说明:打开此开关参数后,使用ParNew+CMS+Serial Old收集器组合进行垃圾收集。Serial Old作为CMS收集器出现Concurrent Mode Failure的备用垃圾收集器。
参考资料 《深入理解Java虚拟机》
因此虚拟机主要关注java堆和方法区的内存分配和回收。
1、对象存活判定
1)引用计数算法
给对象添加一个引用计数器,每当由一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
缺点:很难解决对象相互循环引用的问题(对象相互循环引用,但其实他们都已经没有用了)。
2)可达性分析算法
通过一系列的成为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GC ROOTS没有任何引用链相连时,则证明此对象时不可用的。
Java语言中GC Roots的对象包括下面几种:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈JNI(Native方法)引用的对象
2、垃圾收集算法
1)标记-清除算法
算法分为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
不足:一个是效率问题,标记和清除两个过程的效率都不高;另一个是空间问题,标记清楚之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后再程序运行过程中需要分配较大的对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
2)复制算法
将可用内存按照容量划分为大小相等的两块,每次只使用其中的一块。当这块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可。
不足:将内存缩小为了原来的一半。
实际中我们并不需要按照1:1比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor
当另一个Survivor空间没有足够空间存放上一次新生代收集下来的存活对象时,这些对象将直接通过分配担保机制进入老年代。
3)标记-整理算法
类似于标记-清除,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存。
3、Java虚拟机垃圾收集器
1)Java内存分带划分
Java虚拟机根据对象存活的生命周期长短,将java堆划分为新生代、老年代。而方法区主要是持久代,也参与垃圾回收。
参数设置:
-Xms128M :设置堆最最小128M
-Xmx512M :设置堆最大512M
-Xmn20M :设置新生代20M
-XX:SurvivorRatio=8 :设置Eden区和Survivor区的大小(8:1),分别为16M、2M
-XX:PermSize=64M:设置持久代大小64M
-XX:MaxPermSize=128M:设置持久代最大128M
2)垃圾收集器
可以看到,新生代收集器有Serial、ParNew、Parallel Scanenge收集器,老年代收集器有CMC、Serial Old、Parallel Old收集器。G1收集器是一个全带收集器,代表当前收集器技术发展的前沿成果。
Serial收集器:
采用复制算法 ,新生代收集器。
这个收集器是一个单线程的收集器,但它的单线程的意义不仅仅说明它会只使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。
ParNew 收集器:
采用复制算法 ,新生代收集器。
ParNew收集器是Serial收集器的多线程版本,除了使用了多线程进行收集之外,其余行为和Serial收集器一样。
并行:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
并发:指用户线程与垃圾收集线程同时执行(不一定是并行的,可能会交替执行),用户程序在继续执行,而垃圾收集程序运行于另一个CPU上。
Parallel Scavenge 收集器:
采用复制算法 ,新生代收集器。
Parallel Scavenge收集器是一个新生代收集器,它是使用复制算法的收集器,又是并行的多线程收集器。该收集器的目标是达到一个可控制的吞吐量,而
非其他收集器的尽可能缩短垃圾收集时用户线程的等待时间。
吞吐量:就是CPU用于运行用户代码的时间与CPU总消耗时间的比值。即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。
参数 -XX:MaxGCPauseMillis参数用于控制最大垃圾收集停顿时间。
参数 -XX:GCTimeRatio直接设置吞吐量大小(垃圾收集时间比率,吞吐量的倒数)。
Serial Old 收集器:
采用标记整理的算法,老年代收集器。
Serial Old 是Serial收集器的老年代版本,是一个单线程收集器。
Parallel Old 收集器:
采用标记整理的算法,老年代收集器。
Parallel Old收集器是Paraller Scavenge收集器的老年代版本,使用多线程和标记整理算法。
CMS收集器:
采用标记清除算法,老年代收集器。
CMS收集器是基于标记清除算法实现的,整个过程分为4个步骤:
1.初始标记2.并发标记3.重新标记4.并发清除。其中,初始标记和重新标记这两步骤仍然需要“Stop the world”。
优点:并发收集、低停顿。
缺点:1.CMS收集器对CPU资源非常敏感,CMS默认启动的回收线程数是(CPU数量+3)/4。
2.CMS收集器无法处理浮动垃圾,可能出现Failure失败而导致一次Full Gc地产生,得搭配Serial Old使用。
3.CMS是基于标记清除算法实现的,存在碎片问题。
(3)收集器搭配使用及参数设置
在当前jdk中,主要分为server模式和client模式,默认是client模式,而在不同的模式中默认的垃圾收集算法也是不一样的。在client模式中,新生代和老年代默认是Serial和Serial Old,在server模式中默认的新生代和老年代收集器是Parallel
Scavenge+Serial Old。java虚拟机的-XX:+PrintGCDetails参数可以打印垃圾收集器的日志信息。
使用串行收集器
参数: -XX:+UseSerialGC
说明:虚拟机运行在Client模式的默认值,打开此开关参数后,使用Serial+Serial
Old收集器组合进行垃圾收集。
使用ParNew并行收集器
参数:-XX:+UseParNewGC
说明:打开此开关参数后,使用ParNew+Serial Old收集器组合进行垃圾收集。
使用并行收集器
参数:-XX:+UseParallelGC
-XX:+UseAdaptiveSizePolicy
:java虚拟机动态自适应策略,动态调整年老代对象年龄和各个区域大小。
-XX: ParallelGCThreads:设置并行GC时进行内存回收的线程数。
说明:打开此开关参数后,使用Parallel
Scavenge+Serial Old收集器组合进行垃圾收集。
使用并行Old收集器
参数:-XX:+UseParallelOldGC
-XX:+UseAdaptiveSizePolicy
:java虚拟机动态自适应策略,动态调整年老代对象年龄和各个区域大小。
-XX: ParallelGCThreads:设置并行GC时进行内存回收的线程数。
说明:打开此开关参数后,使用Parallel
Scavenge+Parallel Old收集器组合进行垃圾收集。
使用并发收集器
参数:-XX:+UseConcMarkSweepGC
-XX:+CMSInitiatingOccupancyFraction
默认68%,控制当老年代空间使用率超过该值,启动垃圾收集。
-XX:+UseCMSCompactAtFullCollection 默认开启,设置CMS收集器在完成垃圾收集之后是否进行一次内存整理。
-XX:+CMSFullGCsBeforeCompaction 默认值0,设置CMS收集器在进行多少次垃圾收集不压缩整理之后才进行一次内存整理。
说明:打开此开关参数后,使用ParNew+CMS+Serial Old收集器组合进行垃圾收集。Serial Old作为CMS收集器出现Concurrent Mode Failure的备用垃圾收集器。
参考资料 《深入理解Java虚拟机》
相关文章推荐
- .NET垃圾回收器(GC)原理浅析
- php引用计数器进行垃圾收集机制介绍
- 深入理解PHP原理之Session Gc的一个小概率Notice
- 浅析Java中的GC垃圾回收器的意义及与GC的交互
- 浅谈关于Java的GC垃圾回收器的一些基本概念
- Android垃圾回收机制及程序优化System.gc
- Java虚拟机JVM性能优化(三):垃圾收集详解
- 从JVM的内存管理角度分析Java的GC垃圾回收机制
- Java中垃圾回收器GC对吞吐量的影响测试
- 全面解析Java中的GC与幽灵引用
- Java堆空间占满的gc日志实例
- 大话JMM
- JVM中的垃圾收集器
- java.lang.OutOfMemoryError: GC overhead limit exce
- GC知识
- 垃圾收集器与内存分配策略
- java性能优化 –gc日志收集与分析
- java7中G1垃圾收集器使用和流程总结
- java 虚拟机--新生代与老年代GC