> 文章列表 > JVM系统优化实践(17):线上GC案例(二)

JVM系统优化实践(17):线上GC案例(二)

JVM系统优化实践(17):线上GC案例(二)

您好,我是湘王,这是我的CSDN博客,欢迎您来,欢迎您再来~


GC的概念并不难明白,而且它的原理也不复杂,但是很难用好。为什么?因为每个平台、架构、指标、运行环境都不一样,甚至(新手)工程师的编码习惯都不一样,随手的一段代码可能就给运维优化带来巨大压力。所以除了需要充分做压测根据实际情况合理设置外,就是大量接触实际案例。那么接下来,会有很多案例陆续发布。

一次线上大促,需要在特定时间段,给所有用户发短信、邮件及其他消息,以此来吸引比平时多几倍的大量用户参与,通过监控系统,得知CPU卡死,而且更严重更牛逼的问题是:重启无效!

我们知道,CPU负载过高的常见原因不外乎:

1、创建了大量的线程且同时并发运行;

2、二、JVM频繁执行GC。

在排查完代码,确定不是因为代码原因造成线程卡死之后,再来排查Full GC问题。使用内存分析工具:MAT。先用jmap导出内存快照,再用jhat或MAT插件分析。MAT插件可以在eclipse的官网下载。

MAT使用方法:

1、修改MemoryAnalyzer.ini文件

-startup

plugins/org.eclipse.equinox.launcher_1.5.0.v20180512-1130.jar

--launcher.library

plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.700.v20180518-1200

-vmargs

-Xmx1024m

2、如果通过jmap导出的dump文件比较大(例如几个G),那么务必在使用MAT之前,先给MAT设置一下堆内存大小,默认1024M。

jmap -F -dump:live,file=dump.hprof [PID](耗时较长)

或者

jmap [PID](快速生成)

 

1、Leak Suspects是基于内存泄漏的分析,尝试找出导致内存泄漏的一批对象

2、Component是分析对象问题。

通过使用MAT,最终定位问题是:由于线上系统使用了缓存,但既没有限定缓存空间大小,也没有指定LRU算法,导致空间耗尽。

再来,继续。

一次平台版本升级后,系统无法使用,发现是因为CPU负载飙升。

问题定位与排查:

1、通过jstat,发现Full GC每两分钟执行一次,且异常耗时

2、JVM的配置为10G年轻代(Eden:S0:S1 = 8:1:1),10G老年

3、Eden一分钟就被塞满,且每次Young GC后有几个G的对象进入老年代

4、这说明系统在运行时产生大量的对象,且处理极其缓慢

5、这明显是代码层面的问题,仅靠JVM优化没有用

使用MAT通过分析dump文件的内存泄漏问题,找到问题代码,由于JDK不同版本的特性导致String.split()产生问题。

现在再来整体回顾一下JVM技术的一些基本内容。

一、JVM的GC的运行原理

1、JVM的内存区域划分为年轻代、老年代和元空间;

2、年轻代又分为Eden区、S0、S1;

3、系统运行过程中,会不停在Eden区中创建各种对象;

4、当Eden区塞满之后,执行Young GC,使用复制算法,在S0和S1之间来回复制存活对象;

5、对象在如下情况会进入老年代:年龄划分达到一定阈值、大对象、Young GC后存活对象S0/S1放不下、动态年龄判定规则;

6、老年代Full GC触发规则:老年代已用空间大于老年代空间阈值、Young GC前:老年代可用空间小于历次Young GC后进入老年代的对象的平均大小、Young GC后:老年代空间放不下Young GC后存活对象;

7、老年代的垃圾回收通常使用CMS垃圾回收器;

8、JVM优化的核心内容就是减少Full GC的频率和耗时;

二、如何设置JVM参数

1、首先应该估算每个核心接口每秒的请求次数,以及每次请求所占用的空间大小,进而估算每秒会占用多少空间;

2、估算出Eden区多久会填满,进而估算出多久执行一次Young GC;

3、估算多少对象会升入老年代,用多少空间,多久触发Full GC;

4、通过这样的方式,就可以合理分配年轻代和老年代的空间。

三、JVM参数设置原则

1、尽可能让Young GC后存活的对象远小于S0/S1;

2、尽可能避免对象进入或频繁进入老年代;

3、最理想状态,就是老年代空间使用稳定,几乎不发生Full GC。

四、压测之后合理设置JVM参数

用jstat观察JVM的运行过程,尤其要注意:Eden区的增长速率、Young GC频率、Young GC耗时、Young GC后多少对象存活、老年代增长速率、Full GC频率、Full GC耗时

五、频繁发生Full GC的几种病症(这只是一种参考,就像体温升高不一定就是感冒一样)

1、机器CPU负载过高;

2、频繁Full GC报警;

3、系统响应缓慢。

六、频繁发生Full GC的几种原因

1、系统并发过高,数据处理量过大,导致Young GC频繁,且每次Young GC后存活对象过多;

2、内存空间分配不合理;

3、一次性加载过多数据进入内存;

4、系统发生内存泄漏;

5、元空间的类加载过多;

6、误用System.gc()(System.gc()不是一定不能用,但一定得有前提条件。很多新手喜欢炫技,结果是给自己找麻烦)。

七、JVM中的一些痛点

1、工作中的JVM优化实践;

2、线上系统如何监控GC及定位、分析、解决GC问题;

3、将之前学过的内容全部串起来;

4、将系统负载提高到10倍、100倍来考虑GC问题;

5、把GC问题和业务相结合,深度思考,按照JVM内存运行模型总结几个可能发生的性能问题,然后真理出一套方案;

6、结合业务,总结归纳出公司级别的JVM模板。

八、JVM优化时的注意事项

很多博客会设置一些非常少见的JVM参数,完全是误导。真正的JVM优化:依据业务选择合适的垃圾回收器(如ParNew、CMS、G1),以及经过压测之后根据实际表现调整GC的参数设置。

FAQ:

1、jxl导出excel的时候,会默认调用gc方法,需要注意;

2、jstat一般用于死锁情况的分析,看是哪个线程卡住了;

3、当多个服务竞争某个资源时,可以考虑加上分布式锁,如果并发量高,可以考虑拆分服务,提升吞吐量;

4、一般的redis分布式锁都可以用redisson框架实现;

5、如果选择减少G1的耗时,那么也就意味着G1的GC次数增多;

6、频繁发生Full GC,并不一定是空间太小,甚至根本不是空间问题,需要结合实际业务情况多分析多思考;

7、实际内存消耗往往比数据本身要大很多。

如果上面的看不明白就往回翻,看以前的文章。


感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~