GC浅析之二HotSpot GC了解

HotSpot JVM 内存模型

  • 内存概念上如下划分
    • HotSpot内存参数
    • -Xms; -Xmx 堆大小
    • -Xss 栈大小
    • -Xmn --XX:SurviorRatio=xxx 新生代大小 ;新生代中eden和survivor的比例
    • -XX:NewRatio=xx;-XX:MaxTenuringThreshold 新生代和永久代的比例,新生代最大存活次数
    • -XX:PermSize=xxx; -XX:MaxPermSize=xxx 永久代大小
  • HotSpot JVM 参数可以分为标准参数(standard options)和非标准参数(non-standard options);
  • 非标准参数则有因升级JDK而改变的可能

JVM 堆的申请和管理

  • JVM 通过mmap系统调用向kernel申请,然后切分成不同的generation,之后vm通过virtual space进行管理,实现Java Heap的expand和shrink。
  • 一次性申请大小:Xmx + MaxPermSize + 其他

Java对象模型(linux 64bit

  • 规则
  • 举例:一个HashMap对象图模型

HotSpot JVM GC策略可以按照下面几个维度总结

  • GC工作单线程、多线程进行
    • GC算法的实现中使用单线程完成内存回收工作,GC算法不需要解决同步等问题,缺点在于效率低下,没有利用多CPU资源
    • GC算法通过多线程实现,充分利用多CPU资源,效率高
  • JVM GC 是否和应用并发执行
    • stop the world: JVM GC时停止应用---影响应用的latency响应时间
    • 并发:JVM GC 和 应用一起在执行---会存在回收不干净,需要二次回收情况,即影响吞吐量
    • 吞吐量 or latency的取舍:
      • 吞吐量:垃圾回收时间与非垃圾回收时间的比值 > 通过-XX:MaxGCPauseMillis=<N>指定。为毫秒.如果指定了此值的话,堆大小和垃圾回收相关参数会进行调整以达到指定值。不用显示指定使用什么垃圾收集器
      • 最大垃圾回收暂停:指定垃圾回收时的最长暂停时间 > 通过-XX:GCTimeRatio=<N>来设定,公式为1/(1+N)
  • 如何处理内存回收后的碎片问题
    • 保证GC后无碎片 压缩(标记-整理)GC算法 && 拷贝GC算法来实现
    • 有碎片 不压缩(标记-删除)GC算法

HotSpot JVM GC 算法

    1. DefNew

    2. MSC

    3. PS

    4. PS MarkSweep

    5. ParNew

    6. CMS

  • Copy
    • 单线程,拷贝算法,不会产生内存碎片
    • 用于young(eden+from),Minor GC
    • 触发条件:Eden区满
    • 算法流程: 根据对象的应用关系图,从root对象开始,只是move&copy活的对象到survivor to。(判断Young区对象是否活着也要参考old区的对象,利用card table)然后回收eden和survivor from
  • MSC(Mark-Sweep-Compact)
    • 单线程,标记-整理算法,不会产生内存碎片
    • 用于young(eden+from)+old+perm,Full GC
    • 触发条件:
      • old区满;perm区满
      • promotion failed
      • concurrent mode failure
      • YGC时的悲观策略:统计得到Minor GC引起的promotion需要old区空间大于当前old区剩余空间
      • 显示调用System.gc() ,包括RMI等的定时触发
      • dump live的内存
    • 算法流程4阶段,单线程串行执行,每个阶段依次扫一遍内存

1.        单线程执行,从root开始深度优先搜索标记所有活的对象(等于做了CMS中的initial-mark和concurrent-mark,remark的活儿,但是是单线程且stop-the-world)

2.        计算所有标记的活对象新的内存地址(压缩后的位置)

3.        修改对象的内部引用,因为活对象要被压缩移到新内存位置了

4.        压缩移动对象(memcopy),根据第二阶段计算的位置

             PS Scavenge

             用于young(eden+from)区,Minor GC

             多线程版本的Copy GC算法,减少了停机时间

             PS MarkSweep

             用于old、perm区, Full GC

             此算法将整个old划分为多个region,并且基于一个假设:

经过上次GC compact后,越左边的区域,活对象密度要远远大于右边的区域。
所以就从左往右分析,当某个区域的密度达到一个值的时候,就认为这是一个临界区域,所以这个临界区域左边的区域,将不会进行压缩,而右边的区域,则会进行压缩。

             算法流程

0.        old或perm区被划分成为固定大小的多个region

1.        Marking phase:多线程标记活对象

2.        Summary phase:单线程执行,从左到右依次检查每个region的活对象密度,直到找到某个region的密度到了一个值得进行compact的值。此region的左侧区域叫做dense prefix,右侧region需要compact,并且计算了每个需要compact的region的压缩点信息。

3.        Compact phase:多线程执行region compact

             ParNew

             concurrent collector(concurrent low pause collector) 即Concurrent Mark-Sweep (CMS) Collector中的回收新生代的GC算法

             用于young(eden+from)区,Minor GC

             ParallelScavenge和ParNew都是并行GC,主要是并行收集young gen,目的和性能其实都差不多,区别如下

0.        PS以前是广度优先顺序来遍历对象图的,JDK6的时候改为默认用深度优先顺序遍历,并留有一个UseDepthFirstScavengeOrder参数来选择是用深度还是广度优先。在JDK6u18之后这个参数被去掉,PS变为只用深度优先遍历。ParNew则是一直都只用广度优先顺序来遍历

1.        S完整实现了adaptive size policy,而ParNew及“分代式GC框架”内的其它GC都没有实现完(倒不是不能实现,就是麻烦+没人力资源去做)。所以千万千万别在用ParNew+CMS的组合下用UseAdaptiveSizePolicy,请只在使用UseParallelGC或UseParallelOldGC的时候用它。

2.        由于在“分代式GC框架”内,ParNew可以跟CMS搭配使用,而ParallelScavenge不能。当时ParNew GC被从Exact VM移植到HotSpot VM的最大原因就是为了跟CMS搭配使用。Reference

             CMS

             标记-清除算法,会产生内存碎片

             用于old和Perm

             触发条件: 1. 保证先于Serial MSC之前触发

0.        –XX:CMSInitiatingOccupancyFraction=n 默认值68,当old区超过这个上限 则触发

1.        CMSClassUnloadingEnabled 考虑perm区大小决定触发

2.        根据JVM的统计信息决定

3.        ....

             算法流程

0.        inital mark: 1st pause,stop-the-world 仅仅标记root对象,在bitmap里面标记,单线程

1.        concurrent marking: 不停机 多线程从初始活对象开始递归遍历标记所有活对象

2.        preclean: 不停机 由于第二阶段不停机,由第一阶段标记的对象的引用状态可能变化了,remark阶段要再次停机标记,这里为了让remark阶段停机时间短一些而工作

3.        abort-clean: 不停机

4.        remark: 2nd pause,stop-the-world 由于concurrent marking阶段是不停机的,此时理论上存在新的活对象,此时要重新标记一次,但是应该工作量会小很多。多线程执行,确保所有活对象都被标记

5.        concurrent sweeping: 不停机 单线程的删除无用对象;old区首地址开始一个个对象遍历

6.        reset:CMS GC工作的收尾工作,比如回收bitmap等

             不会进行压缩:

0.        不能使用bump-the-pointer technique,需要一个free list,记录所有被回收的内存。造成在old区分配内存时代价比较大

1.        在concurrent marking阶段是不停机的,所有有可能漏标记新的活对象,所以需要remark的工作;同时也有可能在initial mark阶段标记的活的对象无用了,结果就是误标记了死对象,造成回收不完全

2.        会产生碎片

HotSpot JVM 垃圾回收器

  • GC 程度、范围
    • YGC:新生代的GC;针对young(eden+from)
    • FGC:全堆范围的GC;针对young(eden+from)+old+perm
  • HotSpot JVM提供垃圾回收器以及特点
    • serial collector 串行垃圾收集器

jvm client模式默认;参数 -XX:+UseSerialGC 显式设置

      • DefNew,MSC
    • parallel collector( throughput collector ) 并行垃圾收集器

使用-XX:ParallelGCThreads=<N>设置并行垃圾回收的线程数。此值可以设置与机器处理器数量相等

      • J2SE 5.0 update 6 引入

-server模式默认;参数 -XX:+UseParallelGC 显式设置

        • PS Scavenge;MSC
      • J2SE 6.0 改进 Parallel Compacting Collector

设置参数-XX:+UseParallelOldGC,使老生代垃圾回收策略为并行压缩

      • PS Scavenge; PS MarkSweep;MSC
    • concurrent collector(concurrent low pause collector) 即Concurrent Mark-Sweep (CMS) Collector

-XX:+UseConcMarkSweepGC 使用CMS收集器,ParNew用于新生代,CMS用于旧生代、永久代
-XX:+UseCMSCompactAtFullCollection 默认CMS算法不进行压缩,这里打开压缩
-XX:+CMSClassUnloadingEnabled
diamond线上对于CMS的配置如上 还可用的配置
–XX:CMSInitiatingOccupancyFraction=n 默认值68,当old区超过这个上限 则触发
> -XX: CMSMaxAbortablePrecleanTime=5(单位为ms)

        • ParNew, CMS, MSC
posted @ 2013-10-25 12:55  dushuai  阅读(627)  评论(0编辑  收藏  举报