「这是我参与 2022 首次更文挑战的第 9 天,活动详情查看:2022 首次更文挑战」
一阴一阳之谓道,继之者善也,成之者性也。
前言
上一篇文章排查线上 cpu 使用率高的问题 的文章受到了大家的关注,开来关于实战与技巧类的文章是热点,今天就继续给大家分享如何排查内存占用率高的问题。
查找内存占用情况
服务器内存使用率高,这个跟排查 cpu 使用率的思路是一样的,首先需要找到哪个进程的使用率高。还是使用我们的 top 命令来查找:
# 每4秒刷新一次
top -d 4
关注一下 %MEM 列,该列表示的就是当前服务器运行的程序内存的使用情况,目前来看 2104 的 mysql 服务和 9886 的 java 服务运行占用内存较高。%MEM 右侧的列是服务运行的时间, mysql 服务由于我开启了缓存,加之长时间没有重启了,目前已经累计使用cpu时间是 6w+ 分钟了, java 服务刚重启过,所以使用的 cpu 时间是 2 分钟多一点儿。这里的时间不是服务运行时间,而是服务使用 cpu 的时间,而且是多个 cpu 使用时间累加的。
言归正传,我们就查询一个 java 进程使用内存的情况。这里需要说的是,一般不会在线上执行该命令,而是在预发灰度和测试环境执行。
# 根据进程号查询对应的内存占用情况
jmap -histo pid | more
一般情况下,该命令打印的内容较多,会加上 more 命令来看前几个就可以了,打印输出的结果是按照内存占用的大小倒序排列, instances 代表实例数, bytes 代表使用的空间, class name 代表使用的类名称。从结果来看,我们可以知道 String Object 还有 HashMap 等 java 常用的类是比较消耗空间的,特别是使用 spring 等框架后,框架内部大量使用了集合框架。不过排查内存使用过高这些都不是重点,要检查自己项目中所用的类是否在头排出现,当然也可以使用 grep 命令来搜索包路径来看项目中类的内存使用情况。不过我们这种实时抓取是建立在项目还在运行的过程中,如果项目已经宕机了呢,我们就无从获取相关的信息。
这个我们需要在项目启动时配置一些 HeapDump 的信息,以便在服务宕机时获取相关信息。
# 查找相关的命令
java -XX:+PrintFlagsFinal -version | grep Heap
# 配置 HeapDump 的主要参数:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./dump.log
主要配置两项,在发送内存溢出时打印堆栈信息到我们配置的日志路径中,默认是不开启的,所以在启动项目中需要添加这两个配置项。当发生宕机的情况时,获取对应的日志信息,使用 VisualVM 分析工具来获取相关信息,展示项目和 histo 命令是类似的。
java 堆的情况
在最后除了 histo 命令之外,我们也可以使用 heap 命令来获取当前堆的快照信息。
# 打印java 进程堆的概要信息和 GC 算法和配置信息
jmap –heap pid
在下图中,我们可以看到默认的 jvm 各个代的大小配置情况,以及对象在空间中的占用,比如 新生代中的 Eden 区就是 71%, From 和 to 的使用情况就比较低,而且总有一个区是空闲的,老年代的空间占用情况就是 50%多一点。结合快照信息也可以看出 jvm 堆的内存使用情况是否正常。
总结
在本文中,我们了解了如何排查内存的占用情况,以及如何配置应用打印相关的堆内存使用情况。后续会继续跟进这一块儿介绍相关的 java 配置和 linux 相关的操作命令,例如 top 、vmstat 、iostat 、 netstat 等命令,在日常的工作中会有很大的用处。