JVM大小分配原理简介(易于新手理解)。
2017-11-23 07:20
218 查看
JVM大小分配怎样才合理取决于某个具体应用的对象的存活模式。
这涉及到分代式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的辅助统计信息:
这涉及到分代式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的辅助统计信息:
相关文章推荐
- Java内存区域划分、内存分配原理(深入理解JVM一)
- 【Java面试整理之JVM】深入理解JVM结构、类加载机制、垃圾回收GC原理、JVM内存分配策略、JVM内存泄露和溢出
- [课本划重点]深入理解jvm-第3章 垃圾收集器与内存分配策略(1)
- JVM的内存分配原理
- JVM初探- 内存分配、GC原理与垃圾收集器
- 【深入理解jvm】内存分配与回收策略
- 笔记:深入理解JVM 第3章 垃圾回收器与内存分配策略
- 理解Java虚拟机(4)之JVM运行时内存分配管理和对象内存分布
- [课本划重点]深入理解jvm-第3章 垃圾收集器与内存分配策略(3)
- MFC原理简介,新手看懂MFC代码
- JVM原理简介
- 理解Java常量池--从JVM原理上理解字符串的比较
- 上午所学(jvm内存分配和shell排序算法的深入理解)
- 深入理解内存(1):原理简介
- 深入理解JVM之JVM内存区域与内存分配 - HelloCsl
- synchronized与static synchronized 的区别、synchronized在JVM底层的实现原理及Java多线程锁理解
- 基于JVM原理、JMM模型和CPU缓存模型深入理解Java并发编程
- JVM初探- 内存分配、GC原理与垃圾收集器
- 深入理解JVM之JVM内存区域与内存分配
- 查看tomcat为jvm分配的内存大小