知识大全 JDK5.0垃圾收集优化之--Dont Pause

Posted

篇首语:休言女子非英物,夜夜龙泉壁上鸣。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 JDK5.0垃圾收集优化之--Dont Pause相关的知识,希望对你有一定的参考价值。

JDK5.0垃圾收集优化之--Dont Pause  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

  作者简介 肖桦 江南白衣 开源项目SpringSide 春天的旁边发起者

  转自

  原本想把题目更简单的定为 《不要停》的 但还是自己YY一下就算了        Java开发Server最大的障碍 就是JDK 版之前的的串行垃圾收集机制会引起长时间的服务暂停 明白原理后 想想那些用JDK 写Server的先辈 不得不后怕       好在JDK 已开始支持多线程并行的后台垃圾收集算法 JDK 则优化了默认值的设置

  一 参考资料

  Tuning Garbage Collection with the Java Virtual Machine 官方指南     Hotspot memory management whitepaper 官方白皮书     Java Tuning White Paper 官方文档     FAQ about Garbage Collection in the Hotspot  官方FAQ JVM     Java HotSpot 虚拟机中的垃圾收集 JavaOne 上的中文ppt    A Collection of JVM Options JVM选项的超完整收集     二 基本概念

   堆(Heap)

  JVM管理的内存叫堆 在 Bit操作系统上有 G G的限制 而 Bit的就没有

  JVM初始分配的内存由 Xms指定 默认是物理内存的 / 但小于 G

  JVM最大分配的内存由 Xmx指定 默认是物理内存的 / 但小于 G

  默认空余堆内存小于 %时 JVM就会增大堆直到 Xmx的最大限制 可以由 XX:MinHeapFreeRatio=指定 默认空余堆内存大于 %时 JVM会减少堆直到 Xms的最小限制 可以由 XX:MaxHeapFreeRatio=指定

  服务器一般设置 Xms Xmx相等以避免在每次GC 后调整堆的大小 所以上面的两个参数没啥用

   基本收集算法

  复制 将堆内分成两个相同空间 从根(ThreadLocal的对象 静态对象)开始访问每一个关联的活跃对象 将空间A的活跃对象全部复制到空间B 然后一次性回收整个空间A 因为只访问活跃对象 将所有活动对象复制走之后就清空整个空间 不用去访问死对象 所以遍历空间的成本较小 但需要巨大的复制成本和较多的内存     标记清除(mark sweep) 收集器先从根开始访问所有活跃对象 标记为活跃对象 然后再遍历一次整个内存区域 把所有没有标记活跃的对象进行回收处理 该算法遍历整个空间的成本较大暂停时间随空间大小线性增大 而且整理后堆里的碎片很多     标记整理(mark sweep pact) 综合了上述两者的做法和优点 先标记活跃对象 然后将其合并成较大的内存块         可见 没有免费的午餐 无论采用复制还是标记清除算法 自动的东西都要付出很大的性能代价

   分代

  分代是Java垃圾收集的一大亮点 根据对象的生命周期长短 把堆分为 个代 Young Old和Permanent 根据不同代的特点采用不同的收集算法 扬长避短也

  Young(Nursery) 年轻代 研究表明大部分对象都是朝生暮死 随生随灭的 因此所有收集器都为年轻代选择了复制算法      复制算法优点是只访问活跃对象 缺点是复制成本高 因为年轻代只有少量的对象能熬到垃圾收集 因此只需少量的复制成本 而且复制收集器只访问活跃对象 对那些占了最大比率的死对象视而不见 充分发挥了它遍历空间成本低的优点

  Young的默认值为 M 随堆内存增大 约为 / JVM会根据情况动态管理其大小变化      XX:NewRatio= 参数可以设置Young与Old的大小比例 server时默认为 : 但实际上young启动时远低于这个比率?如果信不过JVM 也可以用 Xmn硬性规定其大小 有文档推荐设为Heap总大小的 /

  Young的大小非常非常重要 见 后面暂停时间优先收集器 的论述

  Young里面又分为 个区域 一个Eden 所有新建对象都会存在于该区 两个Survivor区 用来实施复制算法 每次复制就是将Eden和第一块Survior的活对象复制到第 块 然后清空Eden与第一块Survior Eden与Survivor的比例由 XX:SurvivorRatio=设置 默认为 Survivio大了会浪费 小了的话 会使一些年轻对象潜逃到老人区 引起老人区的不安 但这个参数对性能并不重要

  Old(Tenured) 年老代 年轻代的对象如果能够挺过数次收集 就会进入老人区 老人区使用标记整理算法 因为老人区的对象都没那么容易死的 采用复制算法就要反复的复制对象 很不合算 只好采用标记清理算法 但标记清理算法其实也不轻松 每次都要遍历区域内所有对象 所以还是没有免费的午餐啊

   XX:MaxTenuringThreshold=设置熬过年轻代多少次收集后移入老人区 CMS中默认为 熬过第一次GC就转入 可以用 XX:+PrintTenuringDistribution查看

  Permanent 持久代 装载Class信息等基础数据 默认 M 如果是类很多很多的服务程序 需要加大其设置 XX:MaxPermSize= 否则它满了之后会引起fullgc()或Out of Memory 注意Spring Hibernate这类喜欢AOP动态生成类的框架需要更多的持久代内存

   minor/major collection

  每个代满了之后都会促发collection (另外Concurrent Low Pause Collector默认在老人区 %的时候促发) GC用较高的频率对young进行扫描和回收 这种叫做minor collection 而因为成本关系对Old的检查回收频率要低很多 同时对Young和Old的收集称为major collection      System gc()会引发major collection 使用 XX:+DisableExplicitGC禁止它 或设为CMS并发 XX:+ExplicitGCInvokesConcurrent

   小结

  Young minor collection 复制算法

  Old(Tenured) major colletion 标记清除/标记整理算法

  三 收集器

   古老的串行收集器(Serial Collector)

  使用 XX:+UseSerialGC 策略为年轻代串行复制 年老代串行标记整理

   吞吐量优先的并行收集器(Throughput Collector)

  使用 XX:+UseParallelGC 也是JDK server的默认值 策略为      年轻代暂停应用程序 多个垃圾收集线程并行的复制收集 线程数默认为CPU个数 CPU很多时 可用–XX:ParallelGCThreads=减少线程数      年老代暂停应用程序 与串行收集器一样 单垃圾收集线程标记整理

  所以需要 +的CPU时才会优于串行收集器 适用于后台处理 科学计算

  可以使用 XX:MaxGCPauseMillis= 和 XX:GCTimeRatio 来调整GC的时间

   暂停时间优先的并发收集器(Concurrent Low Pause Collector CMS)

  前面说了这么多 都是为了这节做铺垫

  使用 XX:+UseConcMarkSweepGC 策略为      年轻代同样是暂停应用程序 多个垃圾收集线程并行的复制收集      年老代则只有两次短暂停 其他时间应用程序与收集线程并发的清除

   年老代详述

  并行(Parallel)与并发(Concurrent)仅一字之差 并行指多条垃圾收集线程并行 并发指用户线程与垃圾收集线程并发 程序在继续运行 而垃圾收集程序运行于另一个个CPU上

  并发收集一开始会很短暂的停止一次所有线程来开始初始标记根对象 然后标记线程与应用线程一起并发运行 最后又很短的暂停一次 多线程并行的重新标记之前可能因为并发而漏掉的对象 然后就开始与应用程序并发的清除过程 可见 最长的两个遍历过程都是与应用程序并发执行的 比以前的串行算法改进太多太多了!!!

  串行标记清除是等年老代满了再开始收集的 而并发收集因为要与应用程序一起运行 如果满了才收集 应用程序就无内存可用 所以系统默认 %满的时候就开始收集 内存已设得较大 吃内存又没有这么快的时候 可以用 XX:CMSInitiatingOccupancyFraction=恰当增大该比率

   年轻代详述

  可惜对年轻代的复制收集 依然必须停止所有应用程序线程 原理如此 只能靠多CPU 多收集线程并发来提高收集速度 但除非你的Server独占整台服务器 否则如果服务器上本身还有很多其他线程时 切换起来速度就 所以 搞到最后 暂停时间的瓶颈就落在了年轻代的复制算法上

  因此Young的大小设置挺重要的 大点就不用频繁GC 而且增大GC的间隔后 可以让多点对象自己死掉而不用复制了 但Young增大时 GC造成的停顿时间攀升得非常恐怖 比如在我的机器上 默认 M的Young 只需要几毫秒的时间 M就升到 毫秒 而升到 M时 就要到 毫秒了 峰值还会攀到恐怖的 ms 谁叫复制算法 要等Young满了才开始收集 开始收集就要停止所有线程呢

   持久代

  可设置 XX:+CMSClassUnloadingEnabled XX:+CMSPermGenSweepingEnabled 使CMS收集持久代的类 而不是fullgc netbeans performance文档的推荐

   增量(train算法)收集器(Incremental Collector)

  已停止维护 –Xincgc选项默认转为并发收集器

  四 暂停时间显示

  加入下列参数 (请将PrintGC和Details中间的空格去掉 CSDN很怪的认为是禁止字句)

   verbose:gc XX:+PrintGC Details  XX:+PrintGCTimeStamps

  会程序运行过程中将显示如下输出

   : [GC : [ParNew: K > K( K) secs] K > K( K) secs]

  显示在程序运行的 秒发生了Minor的垃圾收集 前一段数据针对新生区 从 k整理为 k 新生区总大小为 k 程序暂停了 ms 而后一段数据针对整个堆

  对于年老代的收集 暂停发生在下面两个阶段 CMS remark的中断是 毫秒

  [GC [ CMS initial mark: K( K)] K( K) secs]

  [ CMS remark: K( K)] K( K) secs]

  再加两个参数 XX:+PrintGCApplicationConcurrentTime XX:+PrintGCApplicationStoppedTime对暂停时间看得更清晰

  五 真正不停的BEA JRockit 与Sun RTS

  Bea的JRockit R 的特色之一是动态决定的垃圾收集策略 用户可以决定自己关心的是吞吐量 暂停时间还是确定的暂停时间 再由JVM在运行时动态决定 改变改变垃圾收集策略         它的Deterministic GC的选项是 Xgcprio: deterministic 号称可以把暂停可以控制在 毫秒 非常的牛 一句Deterministic道尽了RealTime的真谛 不过细看一下文档 ms的测试环境是 GB heap 和 平均  % 的活跃对象(也就是 M)活动对象 个 Xeon GHz  G内存 或者是 个Xeon GHz G内存

  最可惜JRockt的license很奇怪 虽然平时使用免费 但这个 ms的选项就需要购买整个Weblogic Real Time Server的license

  其他免费选项 有

   Xgcprio:pausetime Xpausetarget= ms    因为免费 所以最低只能设置到 ms pause target ms是Sun认为Real Time的分界线     Xgc:gencon 普通的并发做法 效率也不错       JavaOne 上有Sun的 Java Real Time System 的介绍 RTS 基于JDK 在Real Time  Garbage Collctor上又有改进 但还在beta版状态 只供给OEM 更怪

  六 JDK 的改进

  因为JDK 在Young较大时的表现还是不够让人满意 又继续看JDK 的改进 结果稍稍失望 不涉及我最头痛的年轻代复制收集改良

   年老代的标识 清除收集 并行执行标识   JDK 只开了一条收集进程与应用线程并发标识 而 可以开多条收集线程来做标识 缩短标识老人区所有活动对象的时间

   加大了Young区的默认大小 默认大小从 M加到 M 从堆内存的 / 增加到 /

   System gc()可以与应用程序并发执行 使用 XX:+ExplicitGCInvokesConcurrent 设置

  七 小结

   JDK /

  对于服务器应用 我们使用Concurrent Low Pause Collector 对年轻代 暂停时多线程并行复制收集 对年老代 收集器与应用程序并行标记 整理收集 以达到尽量短的垃圾收集时间

  本着没有深刻测试前不要胡乱优化的宗旨 命令行属性只需简单写为

   server Xms<heapsize>M Xmx<heapsize>M XX:+UseConcMarkSweepGC  XX:+PrintGC Details  XX:+PrintGCTimeStamps    然后要根据应用的情况 在测试软件辅助可以下看看有没有JVM的默认值和自动管理做的不够的地方可以调整 如 xmn 设Young的大小 XX:MaxPermSize设持久代大小等

   JRockit R

  但因为JDK 的测试结果实在不能满意 后来又尝试了JRockit 总体效果要好些   JRockit的特点是动态垃圾收集器是根据用户关心的特征动态决定收集算法的 参数如下

cha138/Article/program/Java/hx/201311/27195

相关参考

餐厨垃圾处理的收运之困

传统的垃圾收运方式是怎样的?面对近乎打游击似的泔水车,许多正规军往往在竞争中处于下风。“游击军”人数众多,通过人工收集、分散处理的方法也在一定程度上迎合了餐厨垃圾产出单位,满足了他们极大的便利,有的则

餐厨垃圾处理的收运之困

传统的垃圾收运方式是怎样的?面对近乎打游击似的泔水车,许多正规军往往在竞争中处于下风。“游击军”人数众多,通过人工收集、分散处理的方法也在一定程度上迎合了餐厨垃圾产出单位,满足了他们极大的便利,有的则

家庭垃圾分类五大攻略

家庭生活五大攻略,轻松搞定垃圾分类垃圾是放错地方资源的理念,所有的垃圾只要合理的分类收集,分类处理,都将转化为资源。对于家庭来说,合理的分类回收,不仅能减少垃圾排放,还能通过垃圾分类使之重新变成资源。

家庭垃圾分类五大攻略

家庭生活五大攻略,轻松搞定垃圾分类垃圾是放错地方资源的理念,所有的垃圾只要合理的分类收集,分类处理,都将转化为资源。对于家庭来说,合理的分类回收,不仅能减少垃圾排放,还能通过垃圾分类使之重新变成资源。

知识大全 Java垃圾收集算法与内存泄露

Java垃圾收集算法与内存泄露  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  垃圾收集算法的核心

知识大全 Java的垃圾收集机制

Java的垃圾收集机制  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  通常我们把分配出去后却无法

知识大全 有关于JVM的垃圾收集

有关于JVM的垃圾收集  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  Java中使用newnew

知识大全 JVM中垃圾收集技术的变更

JVM中垃圾收集技术的变更  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  随着九十年代java语

知识大全 垃圾自动收集系统指导 (2)

  停止和复制  标记和清除法的兄弟就是停止和复制收集法了停止和复制法解决了标记和清除法的碎片问题但是对内存提出了更高的要求(或者是对一个较小的对象池进行更加频繁的收集)微软的Java虚拟机使用的就是

知识大全 Java虚拟机的垃圾收集算法

Java虚拟机的垃圾收集算法  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  一标记清除算法(Ma