JVM调优总结

本篇从以下几个方面,对JVM调优进行总结

1. YoungGC 频繁

如果线上频繁YoungGC,应该如何解决呢?想有整体思路的话,不防先用反推法,先看原理。

1.1 触发时机

当 JVM 无法为新对象分配在新生代内存空间时总会触发 Young GC。比如 Eden 区占满时,新对象分配频率越高,Young GC 的频率就越高。

1.2 问题的原因

可以发现,当Eden区占满不足以分配新对象时,就会触发YoungGC。

1.3 问题排查

首先先用Jstat工具,观察GC变化,再结合业务逻辑查看是不是新生代设置小了,比如系统每秒产生60M的对象,但是新生代的Eden区只设置为600M,那么不到10秒Eden区就满了,那么在内存允许的范围内,应该加大Eden区的内存,尽量减少YoungGC的次数。

2. FullGC 频繁

2.1 触发时机

  • 老年代空间不足
  • 老年代空间分配担保机制
  • 元空间不够导致的多余full gc
  • 由于新生代的动态年龄判断,导致更多的对象进入老年代

2.2 问题的原因

核心点就一条,老年代的存储空间满了或者将要满了。

2.3 问题排查

根据实际经验,频繁FullGC一般是由于代码问题导致的大对象过多,FullGC之后还是清不掉,所以先用Jmap工具,打印出当时程序里的大对象,然后对照类名,查看代码,看看是不是有从数据库查询出来的一大堆对象,或者某个递归没有终止导致一直创建大对象。

3. OOM问题

3.1 堆内存不足

报错:

java.lang.OutOfMemoryError: Java heap space

原因:

  • 代码中可能存在大对象分配
  • 可能存在内存泄露,导致在多次GC之后,还是无法找到一块足够大的内存容纳当前对象

解决方法:

  • 检查是否存在大对象的分配,最有可能的是大数组分配
  • 通过jmap命令,把堆内存dump下来,使用mat工具分析一下,检查是否存在内存泄露的问题
  • 如果没有找到明显的内存泄露,使用 -Xmx 加大堆内存
  • 还有一点容易被忽略,检查是否有大量的自定义的 Finalizable 对象,也有可能是框架内部提供的,考虑其存在的必要性

3.2 方法栈溢出

报错:

java.lang.OutOfMemoryError : unable to create new native Thread

原因:
出现这种异常,基本上都是创建的了大量的线程导致的,以前碰到过一次,通过jstack出来一共8000多个线程

解决:

  • 通过 *-Xss *降低的每个线程栈大小的容量
  • 线程总数也受到系统空闲内存和操作系统的限制,检查是否该系统下有此限制:
    /proc/sys/kernel/pid_max
    /proc/sys/kernel/thread-max
    max_user_process(ulimit -u)
    /proc/sys/vm/max_map_count

3.3 永久代/元空间溢出

报错:

java.lang.OutOfMemoryError: PermGen space
java.lang.OutOfMemoryError: Metaspace

原因:

  • 在Java7之前,频繁的错误使用String.intern方法
  • 生成了大量的代理类,导致方法区被撑爆,无法卸载
  • 应用长时间运行,没有重启

解决方案:

  • 检查是否永久代空间或者元空间设置的过小
  • 检查代码中是否存在大量的反射操作
  • dump之后通过mat检查是否存在大量由于反射生成的代理类
  • 放大招,重启JVM

4. 大型系统JVM参数设置

4.1 思路

  • 分析业务系统的访问量,得出每小时访问量
  • 将访问量转化为对象数,查看每个对象的大小
  • 计算业务系统每秒产生的对象大小
  • 根据这些对象的特点确定新生代、老年代大小
  • 观察程序运行情况或压测时查看GC日志,查看YoungGC和FullGC的情况
  • 尽量让短期存活的对象尽量都留在survivor里,不要进入老年代,这样在YoungGC的时候这些对象都会被回收,不会进到老年代从而导致FullGC。
  • 可以适当调整进入老年代的动态年龄判断
  • 遇到问题多翻阅那几条优化原则,对症下药

5. G1收集器优化建议

5.1 不要设置年轻代大小

显式的使用-Xmn设置年轻代的大小,会干预G1的默认行为。

  • G1就不会再考虑设定的暂停时间目标,所以本质上说,设定了年轻代大小就相当于禁用了目标暂停时间
  • G1就无法根据需要增大或者缩小年轻代的大小。既然大小固定了,就无法在大小上做任何改变了

5.2 响应时间指标

不要根据平均响应时间(ART)作为衡量标准去设定XX:MaxGCPauseMillis=<N>选项,而是设定一个想在90%或者以上的时间都会满足这目标的值。也就是说90%的用户,都会在目标时间,甚至更短的时间内得到响应。记住设定的目标时间只是一个目标,不能保证永久都会满足这个目标。

5.3 MixedGC优化

  • -XX:InitiatingHeapOccupancyPercent 指定触发全局并发标记的老年代使用占比,默认值45%,也就是老年代占堆的比例超过45%
  • -XX:G1MixedGCLiveThresholdPercent 指定被纳入Cset的Region的存活空间占比阈值,不同版本默认值不同,有65%和85%。在全局并发标记阶段,如果一个Region的存活对象的空间占比低于此值,则会被纳入Cset。
  • -XX:G1HeapWastePercent 指定触发Mixed GC的堆垃圾占比,默认值5%,也就是在全局标记结束后能够统计出所有Cset内可被回收的垃圾占整堆的比例值,如果超过5%,那么就会触发之后的多轮Mixed GC,如果不超过,那么会在之后的某次Young GC中重新执行全局并发标记。可以尝试适当的调高此阈值,能够适当的降低Mixed GC的频率。
  • -XX:G1MixedGCCountTarget 指定一个周期内触发Mixed GC最大次数,默认值8。
  • -XX:G1OldCSetRegionThresholdPercent 指定每轮Mixed GC回收的Region最大比例,默认10%,也就是每轮Mixed GC附加的Cset的Region不超过全部Region的10%,最多10%,如果暂停时间短,那么可能会少于10%。

5.4 to-space overflow 和 to-space exhausted 问题

当在gc日志中出现to-space overflow或者to-space exhausted消息时,说明没有足够的内存来存储晋升对象或者survivor对象,gc日志示例如下:

924.897: [GC pause (G1 Evacuation Pause) (mixed) (to-space exhausted), 0.1957310 secs]

924.897: [GC pause (G1 Evacuation Pause) (mixed) (to-space overflow), 0.1957310 secs]

要缓解此问题,可以尝试以下调整:

  • 增加-XX:G1ReservePercent 选项的值(并相应地增加总堆),以增加“担保预留空间的大小”
  • 通过减小-XX:InitiatingHeapOccupancyPercent的值来更早地开始标记周期
  • 增加-XX:ConcGCThreads的值,以增加并行标记线程的数量

参考资料

  1. https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • jstat -gccause pid 1 每格1毫秒输出结果 jstat -gccause pid 2000 每格...
    Java小铺阅读 5,087评论 1 2
  • JVM与调优 imooc JVM Markdown JVM的内存结构 运行时数据区 程序计数器PC Regist...
    心释逍遥lx阅读 527评论 0 1
  • 1.一些概念 1.1.数据类型 Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始...
    落落落落大大方方阅读 4,454评论 4 86
  • [转] 数据类型 Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始值,即:他代表...
    蛋蛋迪幽桑阅读 255评论 0 0
  • 一些概念 数据类型 Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始值,即:他代...
    java宝典阅读 321评论 0 0