新生代和老年代怎样的比例比较合适

 

引自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。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
文件上传服务器的JVM调优主要是针对内存管理和GC进行的优化。以下是一些常用的方法和步骤: 1. 设置合适的堆内存大小:通过调整-Xmx和-Xms参数来设置堆内存的最大和初始大小。根据服务器的硬件配置和应用程序的需求,适当增加堆内存可以提高性能,减少GC的频率。 2. 调整新生代年代比例新生代年代比例决定了对象在不同代之间的分配和回收。可以通过调整-XX:NewRatio参数来调整新生代年代比例。 3. 选择合适的垃圾收集器:根据应用程序的特点和性能需求,选择合适的垃圾收集器。常见的垃圾收集器有串行收集器(-XX:+UseSerialGC),并行收集器(-XX:+UseParallelGC)和CMS收集器(-XX:+UseConcMarkSweepGC)等。不同的垃圾收集器在吞吐量、延迟和内存占用等方面有所区别。 4. 监控和分析GC日志:通过启用-XX:+PrintGC和-XX:+PrintGCDetails参数,可以在控制台输出GC日志。通过分析GC日志,可以了解GC的频率、持续时间和内存回收情况,从而优化GC策略和参数设置。 5. 使用堆内存快照进行分析:当JVM发生内存溢出(OOM)异常时,可以通过设置-XX:HeapDumpOnOutOfMemoryError参数来生成堆内存快照。通过分析堆内存快照,可以了解内存泄漏和过度使用的情况,进一步优化内存管理和对象生命周期。 请注意,具体的JVM调优方法和步骤应该根据实际情况进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值