垃圾收集与内存分配 - 《深入理解Java虚拟机》ch03笔记

来源:互联网 发布:软件测试入门书籍 编辑:程序博客网 时间:2024/06/03 14:53

程序计数器、Java虚拟机栈、本地方法栈三个区域是线程私有的,栈帧大小编译时已经确定,随着方法执行而生死,无须考虑垃圾收集。而Java堆和方法区内存使用情况则在运行期动态变化,Java垃圾收集专指这两个区域


一、判断对象已死

1. 引用计数

被广泛使用,如:Python、ActionScript、Microsoft COM等,但Java中未使用,因为很难解决对象循环依赖问题


2. 可达性分析

基于“GC Roots”,如果对象没有到达GC Roots的路径,认为对象已死,需要内存回收。


垃圾收集算法

1. 标记-清除(Mark-Sweep)


2. 标记-整理(Mark-Compact)


3. 复制算法(Copying)


二、垃圾收集器

http://img.my.csdn.net/uploads/201208/19/1345372379_9383.jpg

                                                                          图1

说明:

两个收集器有连线代表能够搭配使用,黑框里面是新生代收集器,红框是老年代收集器。G1可以同时做为新生代和老年代收集器使用


1. Serial收集器

新生代、单线程收集器,使用复制算法。Client模式下默认新生代垃圾收集器,在单个CPU环境,桌面应用场景是很好的选择。


2. Serial Old收集器

是Serial收集器老年代版本,同样单线程,使用标记-整理算法,在Client模式和Serial搭配使用。在Server模式下两个作用,1) JDK1.5及以前与PS搭配使用 2) 作为CMS收集器后被方案,在并发收集出现Comcurrent Mode Failure时使用


3. PS收集器

新生代、多线程,关注吞吐量而不是停顿时间。低停顿适合交互性强的应用,而高吞吐量适合后台的计算任务。提供两个吞吐量相关参数:-XX:MaxGCPauseillis和-XX:GCTimeRatio参数

MaxGCPauseillis代表垃圾收集时,最大停顿时间

GCTimeRatio代表垃圾收集时间占总时间的比率


-XX:+UseAdaptiveSizePolicy开启自适应调节策略,虚拟机根据当前运行情况,动态调整细节参数


4. Parallel Old收集器

是PS收集器的老年代版本,多线程,标记-整理算法。JDK1.6时出现,之前PS只能和Serial Old使用,这个组合在老年代比较大,硬件配置较高性能不一定令人满意。JDK1.6之后,可以与PS收集器搭配使用,形成了名副其实的“吞吐量优先”组合。


5. ParNew

新生代收集器,是Serial收集器的多线程版本,在收集算法,控制参数等很多方面和Serial一样。是Server模式下首选新生代收集器,因为除了Serial只有ParNew能够和CMS配合使用。


6. CMS收集器

CMS(Concurrent Mark Sweep)收集器是以低停顿为目标的,老年代收集器,使用标记-清除算法。在JDK1.5发布,是第一个真正意义上的并发收集器,可以在用户线程运行时,进行垃圾回收工作。


有3个缺陷

1) 对CPU资源敏感:由于是垃圾收集线程与用户线程并发执行,所以堆CPU资源使用量较高

2) 产生内存碎片:标记-清除算法,产生内存碎片,如果大对象无法分配连续空间不得不提前进行Full GC。有-XX:+UseCMSCompactAtFullCollection和-XX:CMSFullGCsBeforeCompaction两个参数可以一定程度缓解此问题。

3) 无法处理浮动垃圾:如果CMS运行期间,内存无法满足用户线程使用,出现Comcurrent Mode Failure,就必须启动备案:临时启用Serial Old重新进行老年代垃圾收集,这样的话,停顿反而变长了。


7. G1收集器

G1(Garbage-First)是JDK1.7中才正式发布的,面向服务端,未来目标是取代CMS收集器。对于内存布局和其他收集器已经很大不同,G1是基于Region来进行内存划分的。而新生代和老年代的概念虽然还存在,但已经不是物理隔离的了。


G1虽然已经正式发布,但还没有太多的应用和测试数据。如果追求高吞吐量,G1不会带来特别好处,对于低停顿时间,G1可以作为一个选择。


三、内存分配与回收

1. 对象优先在新生代Eden区分配

    优先在Eden区分配,当Eden区没有足够空间,则进行一次Minor GC。

    新生代具体划分为三个区域:一个相对大点的区域,称为”伊甸园区(Eden)”;两个相对小点的区域称为”From 幸存区(From Survivor)”和”To 幸存区(To Survivor)”。

    Minor GC过程:在GC前To 幸存区保持清空,对象保存在 Eden和 From 幸存区中(首次Minor GC,From 幸存区也是空的),GC运行时,Eden中的幸存对象被复制到 To 幸存区。针对 From 幸存区中的幸存对象,会考虑对象年龄,如果年龄没达到阀值,对象会被复制到To 幸存区,如果达到阀值对象被复制到老年代。复制阶段完成后,Eden 和From 幸存区中只保存死对象,可以视为清空。如果在复制过程中To 幸存区被填满了,剩余的对象会被复制到老年代中。最后 From 幸存区和 To幸存区会调换下名字,在下次GC时,To 幸存区会成为From 幸存区。


2. 大对象直接进入老年代

    用-XX:PretenureSizeThreshold配置阀值,大于此阀值的对象直接在老年代分配


3. 新生代中长期存活的对象进入老年代

    为新生代对象标记年龄,从Eden区域移动到Survivor区域(From Survivor区域)后,年龄为1,每熬过一次Minor GC,年龄加一。到达-XX:MaxTenuringThreshold配置的阀值后,从From Survivor区域移动到到老年代


4. 动态年龄判断

    如果在Survivor空间中相同年龄对象大小总和大于Survivor空间一半时,大于等于此年龄的对象移动到老年代。


5. 空间分配担保

    老年区为新生代的Minor GC过程作内存上的分配担保。如果Eden和From Survivor对象占用总内存空间小于老年代最大连续内存空间,则Minor确保是安全的(担保成功),进行Minor GC。如果大于,查看JVM的HandlePromotionFailure参数是否允许担保失败,如果不允许则进行Full GC,如果允许则判断老年代最大连续内存空间是否大于历次Minor GC进入老年代对象内存大小,如果大于则进行一次冒险的Minor GC,如果小于则进行Full GC。


注:对于JDK 6 Update 24之后,HandlePromotionFailure参数已无效,直接判断老年代最大连续内存空间是否大于历次Minor GC进入老年代对象内存大小,其余过程不变。

   

0 0
原创粉丝点击