一次java应用线上运维实战

背景:今天深圳项目出现负载狂飙的情况,由我负责主要的运维,简单记录运维的情况

一、首先使用top命令查看java进程对资源的使用情况。
通过%CPU、%MEM的参数信息可以看出当前进程疯狂占用CPU(使用量最高已经彪到了百分之3000多),内存使用情况占用到80%以上,8G内存。

二、首先先着手分析内存会不会溢出,因为当前zabbix报警系统的出发点是内存大于80%将会触发报警
首先使用 jmap -heap PID 看进行堆使用情况,根据Heap Usage的数据显示,当前堆内存使用情况还有剩余1G的内存空间,其中 700m的空闲空间在分布新生待,300m的空间分布在老年代。

附上JVM 内存图
这里写图片描述

在这里可以得出结论,堆内存空间还是相对充足,不会出现堆内存溢出的情况。

思考:jvm内存占用情况=堆内存使用情况+非堆内存使用情况

接下里验证非堆内存使用的情况(也是上图的右边区域)
jstat -gcmetacapacity PID 看meta space 使用情况
结果发现metaspace 占用的内存其实不高,大概只有200m左右

ps -o rss vsz -p PID 根据rss值确认实际占用内容多少

RSZ是Resident Set Size,常驻内存大小,即进程实际占用的物理内存大小, 在现在这个例子当中,RSZ和实际堆内存占用差了1个多G,根据网上资料显示这1个多G的内存组成分别为:

1.JVM本身需要的内存,包括其加载的第三方库以及这些库分配的内存
2.NIO的DirectBuffer是分配的native memory
3.内存映射文件,包括JVM加载的一些JAR和第三方库,以及程序内部用到的。上面 pmap 输出的内容里,有一些静态文件所占用的大小不在Java的heap里,因此作为一个Web服务器,赶紧把静态文件从这个Web服务器中人移开吧,放到nginx或者CDN里去吧。
4.JIT, JVM会将Class编译成native代码,这些内存也不会少,如果使用了Spring的AOP,CGLIB会生成更多的类,JIT的内存开销也会随之变大,而且Class本身JVM的GC会将其放到Perm Generation里去,很难被回收掉,面对这种情况,应该让JVM使用ConcurrentMarkSweep GC,并启用这个GC的相关参数允许将不使用的class从Perm Generation中移除, 参数配置: -XX:+UseConcMarkSweepGC -X:+CMSPermGenSweepingEnabled -X:+CMSClassUnloadingEnabled,如果不需要移除而Perm Generation空间不够,可以加大一点: -X:PermSize=256M -X:MaxPermSize=512M
5.JNI,一些JNI接口调用的native库也会分配一些内存,如果遇到JNI库的内存泄露,可以使用valgrind等内存泄露工具来检测
6.线程栈,每个线程都会有自己的栈空间,如果线程一多,这个的开销就很明显了

因为这次负责的是运维工作,无法实际上从代码层面解决问题,所以采取的抑制内存报警的策略是通过调低堆内存的最大空间,因为堆内存的使用空间还剩很多,另外,检测到了其中一台机子是16g内存,在80%使用时已经报警,实际到了报警情况还有剩余4g的内存空间,是很大的,所以结合上述参考的情况,把-Xms的值调小,原来是-Xms=-Xmx=12g,让内存在被使用时才被占用。

另外对于CPU占用过高的问题,可以通过打印线程栈的方式找到对应的线程:

top -Hp PID 找出最高线程编号
print “%x\n” 28178 获取线程16进制地址
jstack PID |grep -10 (线程十六进制编号)

找出具体类,联系产品组进行修改

猜你喜欢

转载自blog.csdn.net/hayre/article/details/80237701