Wednesday, November 5, 2014

JVM parameters

所有已制定的HotSpot内存管理和垃圾回收算法都基于一个相同的堆内存划分:新生代(young generation)里存储着新分配的和较年轻的对象,老年代(old generation)里存储着长寿的对象。在此之外,永久代(permanent generation)存储着那些需要伴随整个JVM生命周期的对象,比如,已加载的对象的类定义或者String对象内部Cache。

-XX:InitialHeapSize/-Xms & -XX:MaxHeapSize/-Xmx
$ java -XX:InitialHeapSize=128m -XX:MaxHeapSize=2g MyApp

-XX:+HeapDumpOnOutOfMemoryError & -XX:HeapDumpPath
$ java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -XX:OnOutOfMemoryError ="sh ~/cleanup.sh" MyApp

-XX:PermSize and -XX:MaxPermSize
永久代在堆内存中是一块独立的区域,它包含了所有JVM加载的类的对象表示。请注意,这里设置的永久代大小并不会被包括在使用参数-XX:MaxHeapSize 设置的堆内存大小中。

-XX:InitialCodeCacheSize and -XX:ReservedCodeCacheSize
JVM一个有趣的,但往往被忽视的内存区域是“代码缓存”,它是用来存储已编译方法生成的本地代码。

-XX:+UseCodeCacheFlushing
如果代码缓存不断增长,例如,因为热部署引起的内存泄漏,那么提高代码的缓存大小只会延缓其发生溢出。为了避免这种情况的发生,我们可以尝试一个有趣的新参数:当代码缓存被填满时让JVM放弃一些编译代码。不过,我仍建议尽快解决代码缓存问题发生的根本原因,如找出内存泄漏并修复它。


新生代存在的唯一理由是优化垃圾回收(GC)的性能。更具体说,把堆划分为新生代和老年代有2个好处:简化了新对象的分配(只在新生代分配内存),可以更有效的清除不再需要的对象(即死对象)(新生代和老年代使用不同的GC算法)
很多对象的生存时间都很短。同时研究发现,新生对象很少引用生存时间长的对象。结合这2个特点,很明显 GC 会频繁访问新生对象,例如在堆中一个单独的区域,称之为新生代。在新生代中,GC可以快速标记回收”死对象”,而不需要扫描整个Heap中的存活一段时间的”老对象”。
SUN/Oracle 的HotSpot JVM 又把新生代进一步划分为3个区域:一个相对大点的区域,称为”伊甸园区(Eden)”;两个相对小点的区域称为”From 幸存区(survivor)”和”To 幸存区(survivor)”。按照规定,新对象会首先分配在 Eden 中(如果新对象过大,会直接分配在老年代中)。在GC中,Eden 中的对象会被移动到survivor中,直至对象满足一定的年纪(定义为熬过GC的次数),会被移动到老年代。
基于大多数新生对象都会在GC中被收回的假设。新生代的GC 使用复制算法。在GC前To 幸存区(survivor)保持清空,对象保存在 Eden 和 From 幸存区(survivor)中,GC运行时,Eden中的幸存对象被复制到 To 幸存区(survivor)。针对 From 幸存区(survivor)中的幸存对象,会考虑对象年龄,如果年龄没达到阀值(tenuring threshold),对象会被复制到To 幸存区(survivor)。如果达到阀值对象被复制到老年代。复制阶段完成后,Eden 和From 幸存区中只保存死对象,可以视为清空。如果在复制过程中To 幸存区被填满了,剩余的对象会被复制到老年代中。最后 From 幸存区和 To幸存区会调换下名字,在下次GC时,To 幸存区会成为From 幸存区。
总结一下,对象一般出生在Eden区,年轻代GC过程中,对象在2个幸存区之间移动,如果对象存活到适当的年龄,会被移动到老年代。当对象在老年代死亡时,就需要更高级别的GC,更重量级的GC算法(复制算法不适用于老年代,因为没有多余的空间用于复制)
如果新生代过小,会导致新生对象很快就晋升到老年代中,在老年代中对象很难被回收。如果新生代过大,会发生过多的复制过程。我们需要找到一个合适大小,不幸的是,要想获得一个合适的大小,只能通过不断的测试调优。

-XX:NewSize & -XX:MaxNewSize
设置 XX:MaxNewSize 参数时,应该考虑到新生代只是整个堆的一部分,新生代设置的越大,老年代区域就会减少。一般不允许新生代比老年代还大,因为要考虑GC时最坏情况,所有对象都晋升到老年代。(译者:会发生OOM错误) -XX:MaxNewSize 最大可以设置为-Xmx/2

-XX:NewRatio
这种方式的优点是新生代大小会随着整个堆大小动态扩展。例如 -XX:NewRatio=3 指定老年代/新生代为3/1. 老年代占堆大小的 3/4 ,新生代占 1/4
如果针对新生代,同时定义绝对值和相对值,绝对值将起作用。
$ java -XX:NewSize=32m -XX:MaxNewSize=512m -XX:NewRatio=3 MyApp
以上设置, JVM 会尝试为新生代分配四分之一的堆大小,但不会小于32MB或大于521MB

-XX:SurvivorRatio
指定伊甸园区(Eden)与幸存区大小比例. 例如, -XX:SurvivorRatio=10 表示伊甸园区(Eden)是 幸存区To 大小的10倍(也是幸存区From的10倍)
假设幸存区相对伊甸园区(Eden)太小, 相应新生对象的伊甸园区(Eden)永远很大空间, 我们当然希望,如果这些对象在GC时全部被回收,伊甸园区(Eden)被清空,一切正常.然而,如果有一部分对象在GC中幸存下来, 幸存区只有很少空间容纳这些对象.结果大部分幸存对象在一次GC后,就会被转移到老年代 ,这并不是我们希望的.考虑相反情况, 假设幸存区相对伊甸园区(Eden)太大,当然有足够的空间,容纳GC后的幸存对象. 但是过小的伊甸园区(Eden),意味着空间将越快耗尽,增加新生代GC次数,这是不可接受的。

-XX:+PrintTenuringDistribution
指定JVM 在每次新生代GC时,输出幸存区中对象的年龄分布。

-XX:InitialTenuringThreshold, -XX:MaxTenuringThreshold and -XX:TargetSurvivorRatio
-XX:MaxTenuringThreshold=10 -XX:TargetSurvivorRatio=90 设定老年代阀值的上限为10,幸存区空间目标使用率为90%。
有多种方式,设置新生代行为,没有通用准则。我们必须清楚以下2中情况:
1 如果从年龄分布中发现,有很多对象的年龄持续增长,在到达老年代阀值之前。这表示 -XX:MaxTenuringThreshold 设置过大
2 如果 -XX:MaxTenuringThreshold 的值大于1,但是很多对象年龄从未大于1.应该看下幸存区的目标使用率。如果幸存区使用率从未到达,这表示对象都被GC回收,这正是我们想要的。 如果幸存区使用率经常达到,有些年龄超过1的对象被移动到老年代中。这种情况,可以尝试调整幸存区大小或目标使用率。

-XX:+NeverTenure and -XX:+AlwaysTenure
颇为少见的参数,对应2种极端的新生代GC情况.-XX:+NeverTenure , 对象永远不会晋升到老年代.-XX:+AlwaysTenure, 表示没有幸存区,所有对象在第一次GC时,会晋升到老年代。


http://ifeve.com/useful-jvm-flags-part-4-heap-tuning/
http://ifeve.com/useful-jvm-flags-part-5-young-generation-garbage-collection/

No comments:

Post a Comment