引自http://hllvm.group.iteye.com/group/topic/34664
有许多现成的调优经验的介绍。Charlie Hunt写的《Java Performance》一书里有很详细的介绍。中文版就快出了,敬请关注。
其中涉及GC调优的部分在过往的JavaOne里也有session介绍过。请搜这个标题:"Step-by-Step: Garbage Collection Tuning in the Java HotSpot™ Virtual Machine"
不过那种很具体的现成经验毕竟是别人在他们见过的环境里沉淀下来的,并不一定适用于所有情况。所以怎样的调优方法适合自己,还是得理解了系统底层的工作原理然后再在实际环境里加以应用、变通才好。
对HotSpot VM里的GC不熟悉的,至少应该把Sun以前出的HotSpot VM的GC调优白皮书读了。
====================
为啥HotSpot VM里收集有两种概念,一种是young GC/minor GC,另一种是full GC/major GC;为啥后者不是叫old GC?
因为young GC只收集young gen,但full GC会收集整个GC堆。
HotSpot VM的full GC会收集整个Java堆,包括其中的young gen与old gen;同时也会顺便收集不属于Java堆的perm gen。
Young + old + perm构成了HotSpot VM的整个GC堆。至少目前还是这样。
(JDK8里的HotSpot VM就没有perm gen了。请注意。)
CMS在并发模式工作的时候是只收集old gen的。但一旦并发模式失败(发生concurrent mode failure)就有选择性的会进行全堆收集,也就是退回到full GC。
====================
大小分配怎样才合理取决于某个具体应用的对象的存活模式。
这涉及到分代式GC的原理。最初为何要把GC堆划分为多个区域,以不同的频率来收集它们?本来就是为了让每次收集的效率都最大(在收集的区域里尽 可能回收出可用空间)。如果一个应用里对象的存活模式满足弱分代假设,那么把新生对象放在同一个区域里,每次收集这个区域的效率都应该比较高(因为假设是 新生对象活不了多久就死了)。
有人专门研究这个。可以用"java object demography"这组关键字来搜已有资料。
====================
举例:可能很多人都有一种印象,young gen应该比old gen小。笼统说确实如此,因为在最坏情况下young gen里可能所有对象都还活着,而如果它们全部都要晋升到old gen的话,那old gen里的剩余空间必须能容纳下这些对象才行,这就需要old gen比young gen大(否则young GC就无法进行,而必须做full GC才能应付了)。
实际上却不总是这样的。所谓“最坏情况”在很多系统里是永远不会出现的。调优就是要针对实际应用里对象的存活模式来破除这些“最坏情况”的假设带来的限制。
许多Web应用里对象会有这样的特征:
·(a) 有一部分对象几乎一直活着。这些可能是常用数据的cache之类的
·(b) 有一部分对象创建出来没多久之后就没用了。这些很可能会响应一个请求时创建出来的临时对象
·(c) 最后可能还有一些中间的对象,创建出来之后不会马上就死,但也不会一直活着。
如果是这样的模式,那young gen可以设置得非常大,大到每次young GC的时候里面的多数对象(b)最好已经死了。
想像一下,如果young gen太小,每次满了就触发一次young GC,那么young GC就会很频繁,或许很多临时对象(b)正好还在被是使用(还没死),这样的话young GC的收集效率就会比较低。要避免这样的情况,最好是就是把young gen设大一些。
那old gen怎么办?如果是上面说的情况,那old gen至少要足以装下所有长期存活的对象(a);同时也要留出一定的余地用来容纳young GC没能清理掉的临时对象。
这样,最后调整出来的结果很可能young GC反而比old gen大许多。这完全没问题。
只有(a)和(b)的话就完美了,现实中最头疼的就是针对(c)对象的调优。它们或许会经历多次young GC之后仍然存活,于是晋升到old gen;但晋升上去之后或许很快就又死掉了。
这种对象最好能不让晋升到old gen(可以让它们在survivor space里多来回倒腾几次再晋升,也就是想办法增加tenuring threshold;不过HotSpot VM里的GC不让外界对此多插手,想减小MaxTenuringThreshold很容易,想增加实际有效的tenuring threshold就没那么容易了)。但如果真的不让它们晋升,young GC的暂停时间就会增长(在survivor space里来回倒腾对象意味着要来回拷贝,这会花时间)。
所以有一种策略是尽量让这种对象的大部分在young GC中消耗掉(在保持young GC的暂停时间不超过某个预期值的前提下),而“漏”到old gen的那些让诸如CMS之类的并发GC来解决。
总之这里要做一定的tradeoff就是了。
=================
知道了原理之后在现实中要如何实践呢?
首先得了解硬性限制:某个服务器总共有多少内存,其中最多可以分配多少给某个应用程序;有没有一些服务对响应时间有严格要求,有的话限制是多少,之类的。
然后看看应用的特征是怎样的。可以借助一些工具来了解对象的存活情况,例如NetBeans的profiler就有这样的功能(老文档);许多其它主流Java profiler也有类似的功能。
这些工具的精度和性能开销各异,总之自己摸索下看看吧。
情况了解清楚了就可以开始迭代调整各种参数看实际运行的表现如何。迭代到满意为止。
要分析实际GC的运行状况,首要切入点就是分析GC日志。有很多工具能把HotSpot VM的GC日志可视化。我以前一直在用的是这个:GCHisto。
然后像Twitter做的这个工具也可以抽取一些GC的辅助统计信息:https://github.com/twitter/jvmgcprof
=================
那个…上面随便写了些。文字不通顺的地方请轻拍,要整理成“文章”的话又要烧脑细胞了…
没说清楚的地方请另外补充背景知识…
例如这个:http://www.infoq.com/interviews/szegedi-performance-tuning,Attila Szegedi的GC调优经验。
还有这个:http://www.infoq.com/presentations/Understanding-Java-Garbage-Collection,Gil Tene谈GC。
相关推荐
通过调整JVM参数,比如新生代和老年代的比例、Survivor区的大小、MaxTenuringThreshold等,可以优化垃圾回收的性能,减少垃圾回收带来的系统停顿时间,提升应用程序的整体运行效率。此外,合理的内存分配和对象管理...
新生代和老年代的大小可以通过JVM参数进行调整,例如使用-XX:NewRatio来设定新生代和老年代的比例。 新生代的垃圾回收策略还包括对象晋升的阈值,可以通过-XX:MaxTenuringThreshold设置。如果对象在新生代生存的...
JVM内存调优包括设置合适的堆大小、新生代与老年代的比例、设置GC策略等。例如,通过 `-Xms` 和 `-Xmx` 参数来设定堆的初始和最大大小,`-XX:NewRatio` 来调整新生代与老年代的比例,`-XX:+UseConcMarkSweepGC` 来...
对比正常节点的配置,我们可以看到其新生代和老年代的比例是默认的2,这意味着老年代的大小相对较小,GC频率可能更高,但单次GC耗时可能更短。这种配置对于处理瞬时创建大量短生命周期对象的应用来说,可能更为合适...
- **调整NewRatio**:控制新生代和老年代的比例,影响Minor GC和Full GC的发生频率。 - **设置TenuringThreshold**:决定对象晋升到老年代前需要在新生代存活的次数。 - **其他参数**:还有许多其他JVM参数,如...
4. **新生代与老年代比例**:通过`-XX:NewRatio`参数调整新生代与老年代的比例,影响对象晋升的速度和GC压力。 5. ** Survivor区策略**:`-XX:SurvivorRatio`用于设定Survivor区与Eden区的比例,控制对象在新生代中...
G1会自动平衡新生代和老年代,适合大型应用和多核处理器环境。 6. **Parallel Old GC** Parallel Old是Parallel GC的老年代版本,同样追求高吞吐量,但其多线程清理老年代可能导致较高的系统资源消耗。 7. **ZGC ...
JConsole、VisualVM和JMC(Java Mission Control)等工具能帮助我们查看GC日志,分析停顿时间和吞吐量,以便调整垃圾回收器参数,如新生代和老年代的比例、Survivor区大小等。 此外,永久代(Perm Gen)或方法区...
- **JVM参数调整**:如-Xms和-Xmx设置堆的初始大小和最大大小,-XX:NewRatio控制新生代和老年代的比例,-XX:SurvivorRatio设定Eden和Survivor区的比例。 - **GC策略选择**:根据应用需求选择合适的垃圾收集器,如...
- **新生代与老年代比例**:`-XX:NewRatio`设定此比例,影响垃圾回收策略。 - **Eden与Survivor区比例**:`-XX:SurvivorRatio`控制新生代内两块Survivor区的比例。 - **垃圾收集器选择**:通过`-XX:+UseG1GC`等...
优化策略包括调整新生代和老年代的比例,以及设置合适的新生代和Survivor区大小。 - **垃圾收集器(GC)**:选择合适的垃圾收集器组合,如Serial、Parallel、CMS或G1,以及调整GC参数以降低垃圾收集频率和停顿时间...
- 内存调优:调整新生代与老年代比例,设置最大堆大小等。 4. **性能优化** - 对象池:减少频繁创建和销毁对象,提高性能。 - 泛型、枚举和注解:了解它们在内存占用和性能上的差异。 - 并发编程:线程池、锁...
新生代和老年代的比例可以通过-XX:NewRatio参数调整,以平衡GC效率和内存利用率。 3. 年老代与持久代:持久代主要用于存储加载的类信息,其大小可以通过-XX:MaxPermSize参数设置。Java 8以后,永久代被元空间...
- **堆内存**:分为新生代和老年代,新生代主要存放短生命周期的对象,老年代则用于长期存活的对象。调优时需关注新生代与老年代的比例,以及各自的空间大小。 - **栈内存**:每个线程都有独立的栈,用于存储方法...
3. 设置新生代和老年代比例:通过-XX:NewRatio参数调整新生代和老年代的大小比例,以适应不同生命周期的对象分配。 4. 调整Survivor空间比例:-XX:SurvivorRatio参数可以调整Eden和Survivor空间的比例,影响对象...
调优往往涉及调整JVM参数,如-Xms、-Xmx设置堆大小,-XX:NewRatio控制新生代与老年代的比例,-XX:SurvivorRatio设定新生代Eden区和Survivor区的大小。这些参数的合理配置能显著改善应用性能。 6. **新一代的垃圾...
- **垃圾回收参数调优**:通过JVM参数可以调整内存大小、垃圾回收器类型、新生代和老年代的比例等,如-Xms、-Xmx设定堆内存大小,-XX:+UseParallelGC选择垃圾回收器。 总之,理解Java的内存管理和垃圾回收机制对于...
调整JVM参数可以优化GC性能,如`-Xms`和`-Xmx`设定堆大小,`-XX:NewRatio`设定新生代与老年代的比例,`-XX:SurvivorRatio`设定Survivor区与Eden区的比例,`-XX:+UseConcMarkSweepGC`启用CMS收集器等。 理解JVM内存...
堆分为新生代和老年代,新生代用于存放新创建的对象,老年代则存放经历过多次垃圾回收后仍然存活的对象。新生代进一步细分为Eden区、From Survivor和To Survivor区,通过复制算法进行垃圾回收。 5. **方法区...
- 新生代:年轻代GC(Minor GC)频繁进行,调整新生代大小和Survivor区比例可减少对象晋升至老年代。 - 老年代:老年代GC(Major GC或Full GC)较慢,避免频繁触发以减少停顿时间。 - CMS(Concurrent Mark Sweep...