您的位置:首页 > 编程语言 > Java开发

Java HotSpot虚拟机的内存管理(垃圾收集)

2017-06-23 10:05 459 查看
文章主要从垃圾收集来讲解内存的管理

年轻代分为:Eden、两个survior空间:To和From



1.串行收集器(Serial Collector)

年轻代和年老代都是用串行收集,即单线程的,收集器工作时,程序会暂停工作。

从J2SE 5.0 release,当程序不是以"-server"方式启动的,默认都是使用"串行收集器",

当然了,也可以通过指定启动参数"-XX:+UseSerialGC"来指定"串行收集器"。

1.1.使用串行收集器的年轻代

Eden中存活的对象复制到"To"空间中(如果对象太大以致于"To"放不下则会直接复制到年老代)

"From"空间的存活对象也复制到"To"空间中,如果"From"空间中的存活对象已经被收集很多次(次数阀值有默认值,亦可配置),那么会把这部分对象复制到年老化。

如果"To"空间满了,Eden和"From"中存活的对象会复制到年老代。

当完成了一次"年轻代收集",Eden和"From"空间都是空的,"From"和"To"互换,则下次进行"年轻代收集"之前,"To"就是空的了。



.



1.2.使用串行收集器的年老代

在年老化或者永久代,串行收集器使用了"标记-清除-合并"(mark-sweep-compact)的算法收集垃圾对象。

收集器标识出存活的对象,然后清除掉无用的垃圾对象,将存活的对象合并到年老化的前部分,

这样就可以空出后部分邻接块内存给新进来的对象,这样可以减少内存碎片。



2.并行收集器(Parallel Collector)

当机器出现多个CPU,并行收集器,也叫吞吐量收集器,可以充分利用机器的多个CPU。

从J2SE 5.0 release,如果程序以"-server"方式启动的,默认使用"并行收集器",

或者通过指定启动参数"-XX:+UseParallelGC"来指定"并行收集器"。

2.1.使用并行收集器的年轻代

并行收集器跟串行收集器一样收集年轻代,JAVA应用在收集期间也会停止运行(stop-the-world),不过多CPU并行收集可以减少垃圾收集时间,从而提高应用的吞吐量。



2.2.使用并行收集器的年老代

并行收集器跟串行收集器一样单线程地收集年老代,算法也是"mark-sweep-compact"。

3.并行合并收集器(Parallel Compacting Collector)

并行合并收集器是从J2SE 5.0 update 6开始被采用的,它跟并行收集器不同的是年老代的收集算法不同。

如果我们应用想使用这种收集器,可以通过"-XX:+UseParallelOldGC"启动参数来指定使用并行合并收集器。

3.1.使用并行合并收集器的年轻代

年轻代的收集跟并行收集器是一样的。

3.2.使用并行合并收集器的年老代

年老代分为3个步骤:标记、统计、合并。这里用到分的思想,把年老代划分为很多个固定大小的区(region)。

标记阶段(多线程),把所有存活的对象划分为N组(应该与回收线程数相同),每一个线程独立的负责自己那一组,标记存活对象的位置以及所在区(Region)的存活率信息。

统计阶段(单线程),统计每一个区(Region)的存活率,原则上靠前面的存活率较高,从前到后, 找到值得合并的开始位置(绝大多数对象都存活的区不值得合并)。

合并阶段(多线程),依据统计阶段的信息,把存活的对象从一个区(Region)复制到另外一个区(Region)。

并行合并收集器在年老代是多线程处理的,所以比并行收集器中断时间还少,它更适合于对应用中断时间有限制的情况下,

不过,它不适合大量应用共享的机器,并且这些应用不能单独长时间垄断CPU,在这种情况,可以通过"–XX:ParallelGCThreads=n"限制垃圾收集的线程数。

4.并发标记清除收集器(Concurrent Mark-Sweep Collector)CMS

在串行、并行收集器中,虽然年轻代的收集不会中断时间很长,年老代的收集不频繁,但是年老代收集中断比较长,所以出现了CMS收集器,CMS收集器也称低延时收集器。

4.1.使用CMS收集器的年轻代

CMS收集器的年轻代与并行收集器的年轻代一样。

4.2.使用CMS收集器的年老代

初始标记(Initial Mark)、并发标记(Concurrent Mark)、重标记(Remark)、并发清除(Concurrent Sweep)

初始标记:单线程,标记应用所有存活的对象->存活对象集合,应用程序短暂暂停;

并发标记:单线程,此时应用程序也在同时运行(所以叫并发标记),标记初始标记中的"存活对象集合"的存活对象,应用同时运行可能更新一些对象的引用,所以会有"重标记"阶段;

重标记:多线程,对"并发标记"阶段引用被更新过的对象,标记出存活对象,应用程序第二次暂停;

并发清除:单线程,经过之前几个阶段,堆里的垃圾对象已经被标记出来,清除垃圾对象。



在收集年老代的垃圾对象后,CMS并没有复制合并空闲内存,这可以节省垃圾收集时间,不过会存在内存碎片。

CMS收集器并没有像其他收集器那样,等到年老代已经满的时候才收集垃圾,CMS会在年老代未满之前启动收集工作,

因为CMS多次暂停应用标记垃圾对象,总的时间变长,而CMS在进行垃圾收集时应用也在并发运行,所以不能等年老代满了再收集,一般CMS会在年老代达到阀值就进行收集垃圾,

可以通过"-XX:CMSInitiatingOccupancyFraction=n"来设计阀值,n默认是68,即年老代使用达到68%就启动年老代收集。



增量模式

把回收操作分为多个片段,执行一个片段之后释放CPU资源给应用程序,在年轻代收集期间,周期性地接着上次的结果继续回收下去。目的也是减少延时。

当机器只有少量CPU时,这个模式非常有用,比如只有1,2个CPU,可以减少延时。

通过"-XX:+UseConcMarkSweepGC"参数指定CMS收集器,如果希望指定增量模式收集,在"-XX:+UseConcMarkSweepGC"的基础上,再指定参数"-XX:+CMSIncrementalMode"。

译自
http://www.oracle.com/technetwork/java/javase/memorymanagement-whitepaper-150215.pdf?ssSourceSiteId=otncn

5.G1收集器

垃圾优先型垃圾回收器

G1收集器从JDK 7 update 4才支持,它是在"-server"模式下使用的垃圾收集器。

-XX:+UseG1GC设置命令参数指定G1收集器;

-XX:MaxGCPauseMillis=200设置最大的中断时间,默认是200毫秒;

-XX:InitiatingHeapOccupancyPercent=45设置触发标记周期的 Java 堆占用率阈值。默认占用率是整个 Java 堆的 45%。

G1比CMS更好的解决方案,G1会进行合并操作,此外,G1提供了更多的可预测的垃圾收集暂停比CMS收集器,并允许用户指定所需的暂停目标。

内存分成许多固定大小的内存域(每个域是Eden、Survivor、old generation spaces, In addition, there is a fourth type of object known as Humongous regions. ),大小从1M到32M不等的2000个区域,所以G1垃圾收集器最大内存32M x 2000 = 62.5G。

而这些区域在逻辑上是连读的一小块内存,不像CMS那样是物理上是连续的。



5.1.年轻代收集

Young GC时,STW(stop-the-world),经过收集后的存活对象被复制到一个或多个空闲域,达到某个阀值时(默认15)后移到年老代,

完成收集后,根据Young GC的统计信息,包含中断的时间等,来调整Eden和Survivor域的大小,可以合理利用内存,提高收集效率。

Young GC收集是多线程并行的。

5.2.年老代收集

初始标记(Initial Mark):Stop-The-World,Young GC标记Survivor域(根区域)中的对象,这些对象可以引用到年老代的对象;

根区域扫描(Root Region Scanning):在初始标记阶段标记存活的对年老代的引用对象,该阶段java应用并发执行,不会STW,这阶段完成之后,再进行下一次Young GC;

并发标记(Concurrent Marking):在整个堆中标记存活的对象,该阶段java应用并发执行,不会STW,这阶段有可能被Young GC中断;

重标记(Remark):Stop-The-World,使用初始快照算法:snapshot-at-the-beginning (SATB)标记"并发标记"阶段产生的垃圾(因为这个阶段应用同时并发执行),"初始快算法"比CMS的"重标记"阶段更快。

清理(Cleanup):Stop-The-World,统计存活的对象和空的区域,清除Remembered Set;与java应用并发执行,重置空的区域并还给空闲列表;所以这阶段是部分STW,部分并发;

复制(Copying):Stop-The-World,将存活对象复制到新的区域,会在年轻代完成(GC日志:[GC pause (young)]),也可能在年轻代和年老代完成(GC日志:[GC Pause (mixed)],mixed:混合)

5.3.Remembered Set

G1收集器中,Region之间的对象引用以及其他收集器中的新生代和老年代之间的对象引用是使用Remembered Set来避免扫描全堆。G1中每个Region都有一个与之对应的Remembered Set,虚拟机发现程序对Reference类型数据进行写操作时,会产生一个Write Barrier暂时中断写操作,检查Reference引用的对象是否处于不同的Region之间(在分代中例子中就是检查是否老年代中的对象引用了新生代的对象),如果是便通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered
Set中。当内存回收时,在GC根节点的枚举范围加入Remembered Set即可保证不对全局堆扫描也不会有遗漏。

参考
http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/G1GettingStarted/index.html http://www.oracle.com/technetwork/java/javase/memorymanagement-whitepaper-150215.pdf?ssSourceSiteId=otncn http://www.oracle.com/technetwork/cn/articles/java/g1gc-1984535-zhs.html
可参照这个博客,写得不错- JVM 垃圾回收算法及垃圾回收器:
http://blog.brucefeng.info/post/jvm-garbage-collection
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: