JVM垃圾回收算法及分代垃圾收集器
一、垃圾收集器的分类
1、次收集器
Scavenge GC,指发生在新生代的GC,因为新生代的Java对象大多都是朝生夕死,所以Scavenge GC非常频繁,一般回收速度也比较快。当Eden空间不足以为对象分配内存时,会触发Scavenge GC。
一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。
当年轻代堆空间紧张时会被触发,相对于全收集而言,收集间隔较短。
2、全收集器
Full GC,指发生在老年代的GC,出现了Full GC一般会伴随着至少一次的Minor GC(老年代的对象大部分是Scavenge GC过程中从新生代进入老年代),比如:分配担保失败。Full GC的速度一般会比Scavenge GC慢10倍以上。当老年代内存不足或者显式调用System.gc()方法时,会触发Full GC。
当老年代或者持久代堆空间满了,会触发全收集操作。可以使用System.gc()方法来显式的启动全收集,全收集一般根据堆大小的不同,需要的时间不尽相同,但一般会比较长。
3、垃圾回收器的常规匹配
二、常见垃圾回收算法
1、引用计数(Reference Counting)
比较古老的回收算法。原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为0的对象。此算法最致命的是无法处理循环引用的问题。
2、复制(Copying)
此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。此算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间。简图如下:
3、标记-清除(Mark-Sweep)
此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。简图如下:
4、标记-整理(Mark-Compact)
此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。简图如下:
三、分代垃圾收集器
1、串行收集器(Serial)
Serial收集器是Hotspot运行在Client模式下的默认新生代收集器, 它的特点是:只用一个CPU(计算核心)/一条收集线程去完成GC工作, 且在进行垃圾收集时必须暂停其他所有的工作线程(“Stop The World” -后面简称STW)。可以使用-XX:+UseSerialGC打开。 虽然是单线程收集, 但它却简单而高效, 在VM管理内存不大的情况下(收集几十M~一两百M的新生代), 停顿时间完全可以控制在几十毫秒~一百多毫秒内。
2、并行收集器(ParNew)
ParNew收集器其实是前面Serial的多线程版本, 除使用多条线程进行GC外, 包括Serial可用的所有控制参数、收集算法、STW、对象分配规则、回收策略等都与Serial完全一样(也是VM启用CMS收集器-XX: +UseConcMarkSweepGC的默认新生代收集器)。
由于存在线程切换的开销, ParNew在单CPU的环境中比不上Serial, 且在通过超线程技术实现的两个CPU的环境中也不能100%保证能超越Serial. 但随着可用的CPU数量的增加, 收集效率肯定也会大大增加(ParNew收集线程数与CPU的数量相同, 因此在CPU数量过大的环境中, 可用-XX:ParallelGCThreads=<N>参数控制GC线程数)。
3、Parallel Scavenge收集器
与ParNew类似, Parallel Scavenge也是使用复制算法, 也是并行多线程收集器. 但与其他收集器关注尽可能缩短垃圾收集时间不同, Parallel Scavenge更关注系统吞吐量:
系统吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。
停顿时间越短就越适用于用户交互的程序-良好的响应速度能提升用户的体验;而高吞吐量则适用于后台运算而不需要太多交互的任务-可以最高效率地利用CPU时间,尽快地完成程序的运算任务. Parallel Scavenge提供了如下参数设置系统吞吐量:
Parallel Scavenge参数 |
描述 |
-XX:MaxGCPauseMillis |
(毫秒数) 收集器将尽力保证内存回收花费的时间不超过设定值, 但如果太小将会导致GC的频率增加. |
-XX:GCTimeRatio |
(整数:0 < GCTimeRatio < 100) 是垃圾收集时间占总时间的比率 |
XX:+UseAdaptiveSizePolicy |
启用GC自适应的调节策略: 不再需要手工指定-Xmn、-XX:SurvivorRatio、-XX:PretenureSizeThreshold等细节参数, VM会根据当前系统的运行情况收集性能监控信息, 动态调整这些参数以提供最合适的停顿时间或最大的吞吐量 |
4、Serial Old收集器
Serial Old是Serial收集器的老年代版本, 同样是单线程收集器,使用“标记-整理”算法。
5、Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本, 使用多线程和“标记-整理”算法, 吞吐量优先, 主要与Parallel Scavenge配合在注重吞吐量及CPU资源敏感系统内使用;
6、CMS收集器(Concurrent Mark Sweep)
CMS(Concurrent Mark Sweep)收集器是一款具有划时代意义的收集器, 一款真正意义上的并发收集器, 虽然现在已经有了理论意义上表现更好的G1收集器, 但现在主流互联网企业线上选用的仍是CMS(如Taobao、微店).
CMS是一种以获取最短回收停顿时间为目标的收集器(CMS又称多并发低暂停的收集器), 基于”标记-清除”算法实现, 整个GC过程分为以下4个步骤:
1. 初始标记(CMS initial mark)
2. 并发标记(CMS concurrent mark: GC Roots Tracing过程)
3. 重新标记(CMS remark)
4. 并发清除(CMS concurrent sweep: 已死对象将会就地释放, 注意:此处没有压缩)
其中1,3两个步骤(初始标记、重新标记)仍需STW. 但初始标记仅只标记一下GC Roots能直接关联到的对象, 速度很快; 而重新标记则是为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录, 虽然一般比初始标记阶段稍长, 但要远小于并发标记时间.
CMS特点:
1. CMS默认启动的回收线程数=(CPU数目+3)/4,当CPU数>4时, GC线程一般占用不超过25%的CPU资源, 但是当CPU数<=4时, GC线程可能就会过多的占用用户CPU资源, 从而导致应用程序变慢, 总吞吐量降低.
2.无法处理浮动垃圾, 可能出现Promotion Failure、Concurrent Mode Failure而导致另一次Full GC的产生: 浮动垃圾是指在CMS并发清理阶段用户线程运行而产生的新垃圾. 由于在GC阶段用户线程还需运行, 因此还需要预留足够的内存空间给用户线程使用, 导致CMS不能像其他收集器那样等到老年代几乎填满了再进行收集. 因此CMS提供了-XX:CMSInitiatingOccupancyFraction参数来设置GC的触发百分比(以及-XX:+UseCMSInitiatingOccupancyOnly来启用该触发百分比), 当老年代的使用空间超过该比例后CMS就会被触发(JDK 1.6之后默认92%). 但当CMS运行期间预留的内存无法满足程序需要, 就会出现上述Promotion Failure等失败, 这时VM将启动后备预案: 临时启用Serial Old收集器来重新执行Full GC(CMS通常配合大内存使用, 一旦大内存转入串行的Serial GC, 那停顿的时间就是大家都不愿看到的了).
3.最后, 由于CMS采用”标记-清除”算法实现, 可能会产生大量内存碎片. 内存碎片过多可能会导致无法分配大对象而提前触发Full GC. 因此CMS提供了-XX:+UseCMSCompactAtFullCollection开关参数, 用于在Full GC后再执行一个碎片整理过程. 但内存整理是无法并发的, 内存碎片问题虽然没有了, 但停顿时间也因此变长了, 因此CMS还提供了另外一个参数-XX:CMSFullGCsBeforeCompaction用于设置在执行N次不进行内存整理的Full GC后, 跟着来一次带整理的(默认为0: 每次进入Full GC时都进行碎片整理).
7、分区收集- G1收集器
G1(Garbage-First)是一款面向服务端应用的收集器, 主要目标用于配备多颗CPU的服务器治理大内存.
- G1 is planned as the long term replacement for the Concurrent Mark-Sweep Collector (CMS).
-XX:+UseG1GC启用G1收集器.
与其他基于分代的收集器不同, G1将整个Java堆划分为多个大小相等的独立区域(Region), 虽然还保留有新生代和老年代的概念, 但新生代和老年代不再是物理隔离的了, 它们都是一部分Region(不需要连续)的集合.如:
每块区域既有可能属于O区、也有可能是Y区, 因此不需要一次就对整个老年代/新生代回收. 而是当线程并发寻找可回收的对象时, 有些区块包含可回收的对象要比其他区块多很多. 虽然在清理这些区块时G1仍然需要暂停应用线程, 但可以用相对较少的时间优先回收垃圾较多的Region. 这种方式保证了G1可以在有限的时间内获取尽可能高的收集效率.
G1的新生代收集跟ParNew类似: 存活的对象被转移到一个/多个Survivor Regions. 如果存活时间达到阀值, 这部分对象就会被提升到老年代.如图:
其特定是:
一整块堆内存被分为多个Regions.存活对象被拷贝到新的Survivor区或老年代.年轻代内存由一组不连续的heap区组成, 这种方法使得可以动态调整各代区域尺寸.Young GC会有STW事件, 进行时所有应用程序线程都会被暂停.
多线程并发GC.
G1老年代GC特点如下:
并发标记阶段
1.在与应用程序并发执行的过程中会计算活跃度信息.
2.这些活跃度信息标识出那些regions最适合在STW期间回收(which regions will be best to reclaim during an evacuation pause).
3.不像CMS有清理阶段.
再次标记阶段
1.使用Snapshot-at-the-Beginning(SATB)算法比CMS快得多.
2.空region直接被回收.
拷贝/清理阶段(Copying/Cleanup Phase)
1.年轻代与老年代同时回收.
2.老年代内存回收会基于他的活跃度信息.
JVM垃圾回收算法及分代垃圾收集器的更多相关文章
- JVM的stack和heap,JVM内存模型,垃圾回收策略,分代收集,增量收集
(转自:http://my.oschina.net/u/436879/blog/85478) 在JVM中,内存分为两个部分,Stack(栈)和Heap(堆),这里,我们从JVM的内存管理原理的角度来认 ...
- JVM垃圾回收算法解析
JVM垃圾回收算法解析 标记-清除算法 该算法为最基础的算法.它分为标记和清除两个阶段,首先标记出需要回收的对象,在标记结束后,统一回收.该算法存在两个问题:一是效率问题,标记和清除过程效率都不太高, ...
- JVM垃圾回收算法(最全)
JVM垃圾回收算法(最全) 下面是JVM虚拟机运行时的内存模型: 1.方法区 Perm(永久代.非堆) 2.虚拟机栈 3.本地方法栈 (Native方法) 4.堆 5.程序计数器 1 首先的问题是:j ...
- JVM垃圾回收算法及回收器详解
引言 本文主要讲述JVM中几种常见的垃圾回收算法和相关的垃圾回收器,以及常见的和GC相关的性能调优参数. GC Roots 我们先来了解一下在Java中是如何判断一个对象的生死的,有些语言比如Pyth ...
- JVM 垃圾回收算法和垃圾回收器
JVM 垃圾回收算法和垃圾回收器. 一.垃圾回收的区域 栈:栈中的生命周期是跟随线程,所以一般不需要关注. 堆:堆中的对象是垃圾回收的重点. 方法区:这一块也会发生垃圾回收,不过这块的效率比较低,一般 ...
- java架构之路-(12)JVM垃圾回收算法和垃圾回收器
接上次JVM虚拟机堆内存模型来继续说,上次我们主要说了什么时候可能把对象直接放在老年代,还有我们的可能性分析,提出GCroot根的概念.这次我们主要来说说垃圾回收所使用的的算法和我们的垃圾回收器,需要 ...
- JVM垃圾回收算法 及 垃圾收集器
摘自<深入理解Java虚拟机> 一.什么是: GC算法是 方法论,那么垃圾收集器就是具体的 实现. 二.四种 垃圾回收算法 1.标记-清除算法:最基础的收集算法:不足有两点:1标记和清除两 ...
- Java基础:JVM垃圾回收算法
众所周知,Java的垃圾回收是不需要程序员去手动操控的,而是由JVM去完成.本文介绍JVM进行垃圾回收的各种算法. 1. 如何确定某个对象是垃圾 1.1. 引用计数法 1.2. 可达性分析 2. 典型 ...
- 《深入理解java虚拟机》笔记(7)JVM调优(分代垃圾收集器)
以下配置主要针对分代垃圾回收算法而言. 一.堆大小设置 年轻代的设置很关键 JVM中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制:系统的可用虚拟内存限制:系统的可用 ...
随机推荐
- vuex中的dispatch和commit
dispatch:含有异步操作,eg:向后台提交数据,写法: this.$store.dispatch('mutations方法名',值) commit:同步操作,写法:this.$store.com ...
- Python字典和集合的内部实现
1. 哈希表(Hash tables) 在Python中,字典是通过哈希表实现的.也就是说,字典是一个数组,而数组的索引是经过哈希函数处理后得到的.哈希函数的目的是使键均匀地分布在数组中.由于不同的键 ...
- How to compile and install Snort from source code on Ubuntu
http://www.tuicool.com/articles/v6j2Ab Snort is by far the most popular open-source network intrusio ...
- 阿里云云盾服务证书免费CA证书申请与配置 (原)
申请入口(首页导航产品->安全->CA证书服务->立即购买) 免费版选择步骤一步一步来 1.品牌选 Symantec 2.保护类型选择 一个域名(免费版只能一个好像) 3.证书类型选 ...
- windows 服务器硬盘的分区
进入Server 2012的操作系统,打开CMD框,输入:diskmgmt.msc,回车. 操作完第一步后会弹出“磁盘管理”的框.鼠标右键点击红框所在位置,选中“压缩卷”. 在“输入压缩空间量(MB) ...
- python中的lxml模块
Python中自带了XML的模块,但是性能不太好,相比之下,LXML增加了很多实用的功能. lxml中主要有两部分, 1) etree,主要可以用来解析XML字符串, 内部有两个对象,etree._E ...
- VM虚拟机配置固定IP
linux下vmware桥接模式.静态ip上外网的配置 http://blog.csdn.net/zdh_139/article/details/73456654 虚拟机网络改成桥接模式 vi /et ...
- latex 公式 和排版
http://bbs.chinatex.org/forum.php?mod=viewthread&tid=7423 http://blog.sina.com.cn/s/blog_5e16f17 ...
- sitecore系列教程之营销人员和技术人员如何策划与消费者的对话以提升体验?
“每次良好的交谈都要从良好的倾听开始.” - 未知 你是如何听取网站访问者的?你是在倾听还是只是回复? 拥有内容管理系统只是良好网站战略的一个要素.毕竟,内容必须是动态的,及时的和相关的. 当网站访问 ...
- freedom is a kind of responsibility
张维迎教授在2017年7月1日北大国发院2017届毕业典礼上的发言<自由是一种责任> 张维迎:自由是一种责任 本文为张维迎教授在2017年7月1日北大国发院2017届毕业典礼上的发言 ...