JVM参数设置

转载:JVM参数设置

1. 基本概念

HotSpot JVM把新生代分为了三部分:1个Eden区和2个Survivor区(分别叫From和To)。默认比例为8:1:1。一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到老年代中。

在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据它们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到老年代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和“From”区已经被清空。这个时候,“From”和“To”会交换它们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为“To”的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移到到老年代中。

2. GC策略

新生代GC:Minor GC,新生代中的对象基本都是朝生夕死的(80%以上),触发频繁,当Eden区内存不够的时候触发,对新生代进行一次垃圾回收,复制算法:基本思想就是将内存分成两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎块。

老年代GC:Major GC,又称Full GC,不会频繁触发,在进行Major GC前一般都先进行了一次Minor GC,使得有新生代的对象晋入老年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时,也会提前触发一次Major GC进行垃圾回收腾出空间。标记-清除算法:首先扫描一次所有的老年代,标记出存活的对象,然后回收没有标记的对象。Major GC的耗时比较长,因为要扫描再回收。Major GC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。当老年代也满的装不下的时候,就好抛出OOM(Out of Memory)异常。

永久带:指内存的永久保存区域,主要存放Class和Meta(元数据)的信息,Class在被加载的时候被放入永久代。它和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着Class的增多而膨胀,最终抛出OOM异常。在Java8中,永久代已经被移除,被一个称为“元数据区”的区域所取代。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入Native Memory,字符串池和类的静态变量放入Java堆中。这样可以加载多少类的元数据就不再由MaxPermSize控制,而由系统的实际可用空间来控制。

3. GC方式

串行GC(SerialGC):在整个扫描和复制过程采用单线程的方式来进行,适用于单CPU、新生代空间较小及对暂停时间要求不是非常高的应用上,是Client级别默认的GC方式,可以通过-XX:+UseSerialGC来强制指定

并行回收GC(ParallelScavenge):在整个扫描和复制过程采用多线程的方式来进行,适用于多CPU、对暂停时间要求较短的应用上,是server级别默认采用的GC方式,可用-XX:+UseParallelGC来强制指定,用-XX:ParallelGCThreads=4来指定线程数

并行GC(ParNew):与旧生代的并发GC配合使用

并发GC(CMS):老年代GC

HotSpot? JRockit?

4. JVM参数设置

活跃数据

  1. 应用程序Full GC后,老年代占用的Java堆大小

  2. 应用程序Full GC后,永久代占用的Java堆大小

老年代

Java堆大小减新生代大小,建议设置为老年代活跃数据的2~3倍,老年代大小=堆大小-新生代大小

-XX:+PrintTenuringDistribution

显示每次Minor GC时Survivor区中各个年龄段的对象的大小(格式:-XX:+PrintTenuringDistribution

-XX:+PrintGCDetails

显示GC细节(格式:-XX:+PrintGCDetails

-XX:+PrintTenuringDistribution

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

-Xms -Xmx

设置堆初始值和最大值大小(格式:-Xms1024m -Xmx1024m),两个值设为一样大,建议设置为老年代活跃数据大小的3~4倍

-XX:PermSize -XX:MaxPermSize

设置永久代初始值和最大值大小(格式:-XX:PermSize=64m -XX:MaxPermSize=128m),两个值设为一样大,建议设置为永久代活跃数据的1.2~1.5倍,Java8中采用元空间,仅由系统的实际可用空间控制

-XX:NewSize -XX:MaxNewSize

设置新生代的大小(格式:-XX:NewSize=64m -XX:MaxNewSize=128m),建议设置为整个堆大小的1/3或者1/4,两个值设为一样大,建议设置为老年代活跃数据的1~1.5倍,最大值一般不超过堆内存大小的1/2

-XX:TargetSurvivorRatio

幸存区空间目标使用率(格式:-XX:TargetSurvivorRatio=90),如果幸存区使用率经常达到,有些年龄超过1的对象被移动到老年代中。这种情况,可以尝试调整幸存区大小或目标使用率

-Xmn

设置新生代的大小(格式:-Xmn2g),建议设置为老年代活跃数据的1~1.5倍

-XX:NewRatio

设置新生代与老年代的比例(格式:-XX:NewRatio=2),如:-XX:NewRatio=2,则新生代占整个堆空间的1/3,老年代占2/3,即老年代是新生代的2倍,这个值不能<=1,也就是说老年代必须大于新生代

-XX:SurvivorRatio

设置Eden区和其中一个Survivor区的比值(格式:-XX:SurvivorRatio=8),默认值为:-XX:SurvivorRatio=8,即Eden区占新生代空间的8/10,另外两个Survivor区各占1/10,Eden区是To/From的8倍

-XX:InitialTenuringThreshold -XX:MaxTenuringThreshold

设置晋升到老年代的对象年龄的初始值和最大值(格式:-XX:InitialTenuringThreshold=10 -XX:MaxTenuringThreshold=90),每个对象在坚持过一次Minor GC后,年龄就加1

-Xss

设置每个线程堆栈大小(格式:-Xss256k),一般来说如果栈不是很深,1M是绝对够用

-XX:+HeapDumpOnOutOfMemoryError

让虚拟机在发生内存溢出时Dump出当前的内存堆转储快照(格式:-XX:+HeapDumpOnOutOfMemoryError),以便分析使用

-XX:+UseSerialGC

强制指定串行GC(格式:-XX:+UseSerialGC

-XX:+UseParallelGC -XX:ParallelGCThreads

强制指定并行回收GC(格式:-XX:+UseParallelGC -XX:ParallelGCThreads=4

-XX:+NeverTenure -XX:+AlwaysTenure

没有老年代和没有新生代,极端情况下的设置,一般不需要(格式:-XX:+NeverTenure -XX:+AlwaysTenure

最后更新于