likes
comments
collection
share

线上问题排查异闻录-小试牛刀

作者站长头像
站长
· 阅读数 14

前言

哈喽哈喽,不知道还有多少人记得我之前欠了一篇重量级文章来着,就是一直难产的项目难点文章。最近准备重启这个选题了,会先写几篇线上问题处理的文章理一理思路。OOM问题也是老生常谈,最常见的就是堆内存溢出和栈内存溢出,但是本期主要聊聊堆的,因为相对来说手段会更加丰富一些,并且会有一定的方法论总结。栈的话确实没啥好说的,看看堆栈异常日志其实就能猜个七七八八,周五同事遇到这个问题,但是因为他电脑问题,换我电脑运行就没有,最后也没定位出来,素材丢失。除了堆内存溢出,还会补充CPU爆满的解决方法做内容填充,会给大家推荐一些实用的工具,帮助我们更好的定位和解决问题。

正文

如何解决堆内存溢出问题

OOM有很多种情况啊,这里就先讲解最常见也是最容易观测的java.lang.OutOfMemoryError: Java heap space,也就是堆内存溢出。

发现

启动Java程序的时候,最好参数加上-XX:+HeapDumpOnOutOfMemoryError,该参数不影响程序运行,运行时没有任何开销,只有OOM时会自动生成Java Heap Dump(特定时刻 JVM 内存中所有对象的快照)。该文件默认会在运行应用程序同级目录下生成一个格式为hprof的文件,当然也可以使用参数-XX:HeapDumpPath=/data指定生成到data文件夹下。

线上问题排查异闻录-小试牛刀

这里说一下我对于Java程序运行添加参数的一些理解,这是我项目的一个常规启动命令,java -javaagent:/usr/local/app/skywalking_agent_zy/skywalking-agent.jar -Dskywalking.agent.service_name=appName−Dskywalking.collector.backendservice={appName} -Dskywalking.collector.backend_service=appNameDskywalking.collector.backendservice={skywalkingIp}:skywalkingPort−Dskywalking.plugin.toolkit.log.grpc.reporter.serverhost={skywalkingPort} -Dskywalking.plugin.toolkit.log.grpc.reporter.server_host=skywalkingPortDskywalking.plugin.toolkit.log.grpc.reporter.serverhost={skywalkingIp} jvmoption−Dserver.port=8080−Denv=jvmoption -Dserver.port=8080 -Denv=jvmoptionDserver.port=8080Denv={env} -jar /usr/local/app/app.jar。${}占位符这里是在DevOps上面配的,当然大家也没必要关注,嘻嘻。这里这个env是公司框架让配的环境参数,前面Javaagent一堆参数都是skywalking要用的。

除开这些客制化的东西,对于普通的应用,一般配置堆大小相同比较好,因为通常来说一个服务器或者容器只会有一个Java应用,释放内存给谁用呢,是吧,没那必要。JVM初始分配的堆内存由-Xms指定,默认是物理内存的1/64,JVM最大分配的堆内存由-Xmx指定,默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制,空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。因此一般设置-Xms、-Xmx相等以避免在每次GC后调整堆的大小。

定位

拿到hprof文件后,可以选用jvisualvm(Jdk8之后不自带,需要到Github上下载)、JProfiler和IDEA的Profiler(旗舰版才有)打开文件,三者的操作逻辑都是类似的,目前我用的最舒服的是JProfiler,以下就拿JProfiler截图举例。 线上问题排查异闻录-小试牛刀 导入hprof文件到JProfiler之后经过解析,默认会跳到该界面,这里直接选上面的最大对象,继续解析。 线上问题排查异闻录-小试牛刀 这里右键选定比较大的对象后会弹出这样一个框,选择引用-传入引用。为啥是传入引用呢,因为我们要找问题的源头啊,哪里来的才是比较重要的。 线上问题排查异闻录-小试牛刀 找到对应堆栈信息,点击显示更多,即可发现带恶人。 线上问题排查异闻录-小试牛刀 以上就是一次完整的查询过程,如果点开发现都是差不多的内容,为了少点几次,保护鼠标,我建议可以换成旭日图更加便捷地查看 线上问题排查异闻录-小试牛刀 可以观察到相对类型地这个对象比较多啊,这里点击一下这块进入内部查询 线上问题排查异闻录-小试牛刀

如何解决CPU占用高问题

CPU占用高的问题就没有挂了之后自动dump文件的好事了。这时候需要善用jstack、监控和Arthas等工具。

发现

正常来说,咱们会有监控软件去监控服务器的一些性能指标,我这用的是Prometheus+Grafana,非常大众哈。 线上问题排查异闻录-小试牛刀 如图可以观察到一个服务器CPU占用的折线图,配合告警可以及时通知相关人员定位问题。

定位-传统武学

通过上面地监控及时发现问题,接下来就该上手具体的操作了。

  1. top -o %CPU,Linux上按CPU从大到小排序,找到占用最多的PID(这里假设是Java应用) 线上问题排查异闻录-小试牛刀
  2. jstack pid > thread.txt,通过jstack命令打印当前Java应用的堆栈信息 线上问题排查异闻录-小试牛刀
  3. top -Hp pid,通过该命令观察此pid进程中所有线程的CPU占用 线上问题排查异闻录-小试牛刀
  4. 找到线程pid,通过命令printf '%x\n' pid得到转换为16进制的nid 线上问题排查异闻录-小试牛刀
  5. 在jstack获得的文件thread.txt中,找到nid对应的线程堆栈信息,找到对应代码块即可 线上问题排查异闻录-小试牛刀
  6. 通常除了CPU占用过高的线程,还需要重点关注线程状态为BLOCKED、WAITING和TIMED_WAITING的部分 线上问题排查异闻录-小试牛刀

定位-新派宝典

我一开始接触的也是传统武学,啪啪啪一堆命令敲得也是非常麻烦嗷,那有没有开箱即用的好东西呢。没错,那肯定是有的,就是大名鼎鼎的Arthas啦。

  1. 下载Arthas.jar,curl -O arthas.aliyun.com/arthas-boot…
  2. 运行java -jar arthas-boot.jar并选择需要监听的Java应用,图形化很赞 线上问题排查异闻录-小试牛刀
  3. 输入命令dashboard打开看板,随时监控,默认5000ms一刷 线上问题排查异闻录-小试牛刀
  4. 针对上面CPU问题,直接选择Thread系列命令 线上问题排查异闻录-小试牛刀

效果如下,牛中牛中牛,解放双手。相比jstack输出的文件,甚至多了cpuUsage这个参数,更加直观。 线上问题排查异闻录-小试牛刀 Arthas还有很多别的牛逼功能,不仅仅是Jdk工具的一个打包,更是对前者进行了易用性上的极大优化,同时也提供了很多新功能,要知道这玩意才一百多KB啊。

写在最后

上周算是休息了大半周吧,原定于上周末写完的本篇延迟到了今天才发,有点怠惰了,大罪司教大人。本篇通过比较简单的两个例子,介绍了常见的两种线上问题场景,希望大家遇到此类问题,尽可能地从技术角度出发,而不是简单粗暴地堆硬件。文中出现的两个问题均来源于同一个项目,之前有提到过,是我正在重构的一个核心老项目,拜其所此收获了不少素材。但是由于重构伙伴的跳槽跑路,该项目运维以及连带着Flink一套全压在我身上,说实话,最近运维快让我喘不过气来,事太多太杂了。借着这个理由,我上周休息了五天,精神饱满,继续周更了,下一篇会接着该篇去尝试解决频繁GC的问题。

最近情绪比较低落,会慢慢来找回状态,生活不易啊。不过生活也有惊喜,认识了很多有趣的人,今天(4.10)是一位拥有有趣灵魂的朋友的生日,祝他生日快乐!愿世界充满幸福快乐之花,向美好的世界献上祝福!!!