JVM系列5-JVM调优

声明:原创文章,转载请注明出处。http://www.jianshu.com/u/e02df63eaa87

1、JVM参数设置

1.1 设置堆内存大小
  • 参数:-Xmx:最大堆
       -Xms:最小堆
  • 含义:堆大小指的是新生代和老生代大小之和。
  • 备注:Java应用程序运行时,首先会被分配-Xms指定的内存大小,并且尽可能尝试在这个空间段内运行。当内存无法满足程序时,JVM会向系统申请内存,直到内存大小为-Xmx。如果-Xms指定的内存较小,那么JVM GC的频率要更高。
  • 注意:当内存使用量到达-Xms时,会触发Full GC。因此,将-Xms-Xmx设置相等时,会减少系统初期GC次数和耗时。
1.2 设置新生代
  • 参数:-XX:NewSize 新生代初始大小
       -XX:MaxNewSize新生代最大值
       -Xmn 新生代大小
    ,设置此值等于设置相同的-XX:NewSize 和-XX:MaxNewSize
  • 备注:设置不同的-XX:NewSize 和-XX:MaxNewSize可能会导致内存震荡。
1.3 设置持久代
  • 参数:-XX:PermSize 持久代初始大小
       -XX:MaxPermSize持久代最大值
  • 含义:持久代(方法区)不属于堆的一部分。持久代的大小直接决定了可以支持多少个类和多少个变量
1.4 设置线程栈
  • 参数:-Xss 线程栈大小
  • 含义:在线程中进行局部变量分配,以及函数调用时,都需要在栈中开辟空间。若栈的空间太小,则线程在执行时,或许没有足够的空间分配变量或达不到足够的函数调用深度,导致应用异常退出。若栈的空间太大,开启线程所需的成本会上升,系统所支持的线程数则会减少。
  • 备注:如果指定的最大堆的大小,则系统支持最大线程数,与堆的大小有关。如果分配给堆的内存过大,则系统支持的最大线程数则会减小。
1.5 堆的比例分配
  • 参数:-XX:SurvivorRatio 用来设置新生代中Eden区和幸存区(From)的比例
        ** -XX:XX:SurvivorRatio = Eden/From = Eden/To**
       -XX:NewRatio设置老年代和新生代的比例
  • 含义:-XX:SurvivorRatio默认为8,新生代三部分空间比例8:1:1。-XX:NewRatio默认为2,新生代和老年代比例为1:2。

2、常见调优案例

2.1 将新对象留在新生代

Full GC的成本要远远高于Minor GC,所以尽可能将对象分配在新生代。虽然大多数情况下,JVM会尝试在Eden区分配新对象,由于内存紧张问题,可能不得不将年轻代对象提前向老年代压缩。所以,为应用程序分配一个恰当的新生代大小,可最大限度避免新对象直接进入老年代。一般的,当Survivor空间不够或占用量达到50%时,就会将对象进入老年代,可以通过调整参数-XX:TargetSurvivorRatio提高Survivor区的利用率。

以下代码申请了4MB空间:

public class Eden {
    public static void main(String[] args) {
        int[] a, b;
        a = new int[512 * 1024];  // 2MB
        b = new int[512 * 1024];  // 2MB
    }
}

使用JVM参数:-XX:+PrintGCDetails -Xms15M -Xmx15M运行,堆内存输出为:

Heap
 PSYoungGen      total 4608K, used 3325K [0x00000000ffa80000, 0x00000000fff80000, 0x0000000100000000)
  eden space 4096K, 81% used [0x00000000ffa80000,0x00000000ffdbf548,0x00000000ffe80000)
  from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
  to   space 512K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x00000000fff00000)
 ParOldGen       total 10240K, used 2048K [0x00000000ff000000, 0x00000000ffa00000, 0x00000000ffa80000)
  object space 10240K, 20% used [0x00000000ff000000,0x00000000ff200010,0x00000000ffa00000)
 PSPermGen       total 21504K, used 2892K [0x00000000f9e00000, 0x00000000fb300000, 0x00000000ff000000)
  object space 21504K, 13% used [0x00000000f9e00000,0x00000000fa0d3060,0x00000000fb300000)

可以看出,一个新对象已经进入了老年代。
使用参数:-XX:+PrintGCDetails -Xms15M -Xmx15M -Xmn8M运行,分配足够大的新生代,GC输出如下:

Heap
 PSYoungGen      total 7168K, used 5506K [0x00000000ff800000, 0x0000000100000000, 0x0000000100000000)
  eden space 6144K, 89% used [0x00000000ff800000,0x00000000ffd60850,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
 ParOldGen       total 4096K, used 0K [0x00000000ff200000, 0x00000000ff600000, 0x00000000ff800000)
  object space 4096K, 0% used [0x00000000ff200000,0x00000000ff200000,0x00000000ff600000)
 PSPermGen       total 21504K, used 2944K [0x00000000fa000000, 0x00000000fb500000, 0x00000000ff200000)
  object space 21504K, 13% used [0x00000000fa000000,0x00000000fa2e0150,0x00000000fb500000)
2.2 大对象进入老年代

大对象直接出现在新生代可能会扰乱新生代GC,这是因为在新生代尝试分配大对象,可能会导致空间不足,为了能容纳大对象,JVM不得不将新生代中其他年轻的对象移到老年代(或许会移动大量小的年轻对象进人老年代)。

综上,可以将大对象直接分配到老年代,但是许多大对象同时又是非活跃的对象,会对老年代GC产生问题,所以应尽量避免这种情况的发生。

以下代码申请了2MB空间:

public class Eden {
    public static void main(String[] args) {
        int[] a = new int[512 * 1024];  // 2MB
    }
}

由于-XX:PretenureSizeThreshold参数对PS收集器不适用,因此采用ParNew收集器。
使用JVM参数:-XX:+UseParNewGC -XX:+PrintGCDetails -Xms15M -Xmx15M 运行,堆内存输出为:

Heap
 par new generation   total 4608K, used 3481K [0x00000000f9e00000, 0x00000000fa300000, 0x00000000fa350000)
  eden space 4096K,  85% used [0x00000000f9e00000, 0x00000000fa1666e8, 0x00000000fa200000)
  from space 512K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa280000)
  to   space 512K,   0% used [0x00000000fa280000, 0x00000000fa280000, 0x00000000fa300000)
 tenured generation   total 10240K, used 0K [0x00000000fa350000, 0x00000000fad50000, 0x00000000fae00000)
   the space 10240K,   0% used [0x00000000fa350000, 0x00000000fa350000, 0x00000000fa350200, 0x00000000fad50000)
 compacting perm gen  total 21248K, used 2942K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  13% used [0x00000000fae00000, 0x00000000fb0df9c0, 0x00000000fb0dfa00, 0x00000000fc2c0000)
No shared spaces configured.

可以看到,这个对象基本上占据了整个新生代。
使用参数:-XX:+PrintGCDetails -Xms15M -Xmx15M -XX:PretenureSizeThreshold=1M运行,分配足够大的新生代,GC输出如下:

Heap
 par new generation   total 4608K, used 1353K [0x00000000f9e00000, 0x00000000fa300000, 0x00000000fa350000)
  eden space 4096K,  33% used [0x00000000f9e00000, 0x00000000f9f52560, 0x00000000fa200000)
  from space 512K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa280000)
  to   space 512K,   0% used [0x00000000fa280000, 0x00000000fa280000, 0x00000000fa300000)
 tenured generation   total 10240K, used 2048K [0x00000000fa350000, 0x00000000fad50000, 0x00000000fae00000)
   the space 10240K,  20% used [0x00000000fa350000, 0x00000000fa550010, 0x00000000fa550200, 0x00000000fad50000)
 compacting perm gen  total 21248K, used 2942K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  13% used [0x00000000fae00000, 0x00000000fb0df9c0, 0x00000000fb0dfa00, 0x00000000fc2c0000)
No shared spaces configured.

可以看到,新生代空间有很大的空闲,数组a已经被分配到了老年代上。

2.3 设置对象进入老年代年龄

可以通过-XX:MaxTenuringThreshold设置对象进入老年代的年龄。若对象在eden区,经过一次GC后,会被移动到幸存区,相应的年龄+1。当对象达到阈值(默认15)后,就会移动到老年代。

以下代码申请了3MB空间:

public class Eden {
    public static void main(String[] args) {
        byte[] a, b, c;
        a = new byte[1024 * 1024];  // 1MB
        b = new byte[1024 * 1024];  // 1MB
        c = new byte[1024 * 1024];  // 1MB
    }
}

使用JVM参数:
-XX:+UseParNewGC -XX:+PrintGCDetails -Xms15M -Xmx15M -XX:SurvivorRatio=3 运行,堆内存输出为:

[GC[ParNew: 2178K->644K(4096K), 0.0034094 secs] 2178K->1668K(14336K), 0.0034551 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
Heap
 par new generation   total 4096K, used 2922K [0x00000000f9e00000, 0x00000000fa300000, 0x00000000fa350000)
  eden space 3072K,  74% used [0x00000000f9e00000, 0x00000000fa039488, 0x00000000fa100000)
  from space 1024K,  62% used [0x00000000fa200000, 0x00000000fa2a13a0, 0x00000000fa300000)
  to   space 1024K,   0% used [0x00000000fa100000, 0x00000000fa100000, 0x00000000fa200000)
 tenured generation   total 10240K, used 1024K [0x00000000fa350000, 0x00000000fad50000, 0x00000000fae00000)
   the space 10240K,  10% used [0x00000000fa350000, 0x00000000fa450010, 0x00000000fa450200, 0x00000000fad50000)
 compacting perm gen  total 21248K, used 2890K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  13% used [0x00000000fae00000, 0x00000000fb0d2910, 0x00000000fb0d2a00, 0x00000000fc2c0000)
No shared spaces configured.

使用参数:-XX:+UseParNewGC -XX:+PrintGCDetails -Xms15M -Xmx15M -XX:SurvivorRatio=3
-XX:MaxTenuringThreshold=0运行,堆内存输出为:

[GC[ParNew: 2302K->0K(4096K), 0.0026039 secs] 2302K->1665K(14336K), 0.0026645 secs] [Times: user=0.06 sys=0.00, real=0.00 secs] 
Heap
 par new generation   total 4160K, used 2156K [0x00000000f9e00000, 0x00000000fa310000, 0x00000000fa350000)
  eden space 3136K,  68% used [0x00000000f9e00000, 0x00000000fa01b210, 0x00000000fa110000)
  from space 1024K,   0% used [0x00000000fa110000, 0x00000000fa110000, 0x00000000fa210000)
  to   space 1024K,   0% used [0x00000000fa210000, 0x00000000fa210000, 0x00000000fa310000)
 tenured generation   total 10240K, used 1665K [0x00000000fa350000, 0x00000000fad50000, 0x00000000fae00000)
   the space 10240K,  16% used [0x00000000fa350000, 0x00000000fa4f0500, 0x00000000fa4f0600, 0x00000000fad50000)
 compacting perm gen  total 21248K, used 2942K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  13% used [0x00000000fae00000, 0x00000000fb0dfa00, 0x00000000fb0dfa00, 0x00000000fc2c0000)
No shared spaces configured.
2.4 稳定与震荡的堆大小

稳定的堆大小可以减少GC次数,但也会增加每次GC的时间。可以让堆大小在一个区间内震荡,不需要使用大内存时,压缩堆指针,加快GC速度。
-XX:MinHeapFreeRatio:设置堆空间最小空闲比例,默认为40。当堆空间小于此值时,JVM会扩展堆空间。
-XX:MaxHeapFreeRatio:设置堆空间最大空闲比例,默认为70。当堆空间空闲内存大于此值时,JVM会压缩堆空间。
备注:当-Xms和-Xmx相等时,以上两个参数设置无效。

2.5 吞吐量优先

-Xms4G -Xmx4G -Xmn2G -Xss128k -XX:UseParallelGC -XX:ParallelGCThreads=20

2.6 降低停顿案例

-Xms4G -Xmx4G -Xmn2G -Xss128k -XX:UseParNewGC -XX:ConMarkSweepGC
-XX:ParallelGCThreads=20 -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=31
目的是,最大限度将对象留在新生代(Minor GC成本远小于Full GC)。

3、调优总结

3.1 响应时间优先
  • 对于年轻代应尽可能设置大,极端情况下,年轻代GC的频率也是最小的。
  • 对于老年代要减少对象。老年代采用并发收集器。堆大小设置要尽量平衡,若太小,会造成内存碎片、高GC频率等,若太大,需要较长的GC回收时间。
3.2 吞吐量优先
  • 对于年轻代应尽可能设置大,对响应时间没有特别邀请,垃圾收集可并行进行。
  • 对于老年代设置要小,这样可以尽可能回收掉大部分短期对象,减少中期对象,而老年代存放长期对象。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容

  • 参数设置 在Java虚拟机的参数中,有3种表示方法用“ps -ef |grep "java"命令,可以得到当前Ja...
    九问阅读 9,100评论 2 52
  • Java 虚拟机有自己完善的硬件架构, 如处理器、堆栈、寄存器等,还具有相应的指令系统。JVM 屏蔽了与具体操作系...
    尹小凯阅读 1,678评论 0 10
  • 1.一些概念 1.1.数据类型 Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始...
    落落落落大大方方阅读 4,515评论 4 86
  • Java 虚拟机内存模型 程序计数器- Program Counter Register是一块很小内存空间。 由于...
    AlbenXie阅读 509评论 0 2
  • 原文阅读 前言 这段时间懈怠了,罪过! 最近看到有同事也开始用上了微信公众号写博客了,挺好的~给他们点赞,这博客我...
    码农戏码阅读 5,940评论 2 31