Java 性能调优实战
刘超
前金山软件技术经理
59174 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 49 讲
开篇词 (1讲)
模块一 · 概述 (2讲)
结束语 (1讲)
Java 性能调优实战
15
15
1.0x
00:00/00:00
登录|注册

25 | 内存持续上升,我该如何排查问题?

jmap命令
jstack命令
jstat命令
pidstat命令
vmstat命令
top命令
监控JVM内存的常用方法
内存泄漏问题
内存溢出问题
JDK工具
Linux命令行工具
思考题
实战演练
工具
排查内存性能问题
总结

该思维导图由 AI 生成,仅供参考

你好,我是刘超。
我想你肯定遇到过内存溢出,或是内存使用率过高的问题。碰到内存持续上升的情况,其实我们很难从业务日志中查看到具体的问题,那么面对多个进程以及大量业务线程,我们该如何精准地找到背后的原因呢?

常用的监控和诊断内存工具

工欲善其事,必先利其器。平时排查内存性能瓶颈时,我们往往需要用到一些 Linux 命令行或者 JDK 工具来辅助我们监测系统或者虚拟机内存的使用情况,下面我就来介绍几种好用且常用的工具。

Linux 命令行工具之 top 命令

top 命令是我们在 Linux 下最常用的命令之一,它可以实时显示正在执行进程的 CPU 使用率、内存使用率以及系统负载等信息。其中上半部分显示的是系统的统计信息,下半部分显示的是进程的使用率统计信息。
除了简单的 top 之外,我们还可以通过 top -Hp pid 查看具体线程使用系统资源情况:

Linux 命令行工具之 vmstat 命令

vmstat 是一款指定采样周期和次数的功能性监测工具,我们可以看到,它不仅可以统计内存的使用情况,还可以观测到 CPU 的使用率、swap 的使用情况。但 vmstat 一般很少用来查看内存的使用情况,而是经常被用来观察进程的上下文切换。
r:等待运行的进程数;
b:处于非中断睡眠状态的进程数;
swpd:虚拟内存使用情况;
free:空闲的内存;
buff:用来作为缓冲的内存数;
si:从磁盘交换到内存的交换页数量;
so:从内存交换到磁盘的交换页数量;
bi:发送到块设备的块数;
bo:从块设备接收到的块数;
in:每秒中断数;
cs:每秒上下文切换次数;
us:用户 CPU 使用时间;
sy:内核 CPU 系统使用时间;
id:空闲时间;
wa:等待 I/O 时间;
st:运行虚拟机窃取的时间。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了面对内存持续上升的情况时,如何使用常用的监控和诊断内存工具来排查问题。作者详细介绍了Linux命令行工具中的top、vmstat和pidstat,以及JDK工具中的jstat、jstack和jmap的使用方法和参数含义。通过这些工具,读者可以深入了解系统和进程的性能情况,以及JVM中的内存使用情况,从而快速定位内存泄漏等问题。文章还通过实际案例模拟了内存泄漏导致的内存溢出问题,并介绍了如何通过工具定位具体的内存泄漏原因。总结指出,在简单业务场景下排查系统性能问题相对简单,但在复杂场景下需要结合源码做具体分析。最后,作者提出了思考题,引发读者对JVM内存监控方法的思考。整体而言,本文内容丰富,对于需要排查内存问题的技术人员具有很高的实用价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 性能调优实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(35)

  • 最新
  • 精选
  • 每天晒白牙
    放两篇自己在工作中排查JVM问题的两篇文章【非广告,纯技术文】 https://mp.weixin.qq.com/s/ji_8NhN4NnEHrfAlA9X_ag https://mp.weixin.qq.com/s/IPi3xiordGh-zcSSRie6nA

    作者回复: 赞!

    2019-07-18
    11
    92
  • 我已经设置了昵称
    老师是否可以讲下如何避免threadLocal内存泄漏呢

    作者回复: 我们知道,ThreadLocal是基于ThreadLocalMap实现的,这个Map的Entry继承了WeakReference,而Entry对象中的key使用了WeakReference封装,也就是说Entry中的key是一个弱引用类型,而弱引用类型只能存活在下次GC之前。 如果一个线程调用ThreadLocal的set设置变量,当前ThreadLocalMap则新增一条记录,此时ThreadLocal实例没有外部强引用,当发生一次垃圾回收,此时key值被回收,而value值依然存在内存中,由于当前线程一直存在,所以value值将一直被引用。. 这些被垃圾回收掉的key就存在一条引用链的关系一直存在:Thread --> ThreadLocalMap-->Entry-->Value,这条引用链会导致Entry不会回收,Value也不会回收,但Entry中的Key却已经被回收的情况,造成内存泄漏。 我们只需要在使用完该key值之后,通过remove方法remove掉,就可以防止内存泄漏了。

    2019-07-18
    11
    78
  • WL
    请问一下老师内存泄露和内存溢出具体有啥区别,有点不太理解内存泄露的概念。

    作者回复: 内存泄漏是指不再使用的对象无法得到及时的回收,持续占用内存空间,从而造成内存空间的浪费。例如,我们之前在第3讲中聊到的在Java6中substring方法可能会导致内存泄漏情况发生。当调用substring方法时会调用new string构造函数,此时会复用原来字符串的char数组,而如果我们仅仅是用substring获取一小段字符,而原本string字符串非常大的情况下,substring的对象如果一直被引用,由于substring的里面的char数组仍然指向原字符串,此时string字符串也无法回收,从而导致内存泄露。 内存溢出则是发生了OutOfMemoryException,内存溢出的情况有很多,例如堆内存空间不足,栈空间不足,以及方法区空间不足都会发生内存溢出异常。 内存泄漏与内存溢出的关系:内存泄漏很容易导致内存溢出,但内存溢出不一定是内存泄漏导致的。

    2019-07-18
    3
    35
  • 怪盗キッド
    我开源了一个 Java 性能监控工具,就是用 JDK 自带的接口实现的。 GitHub 地址:https://github.com/LinShunKang/MyPerf4J

    作者回复: 👍

    2019-09-22
    2
    19
  • Rain
    老师,为什么线程要sleep一下,看了注释还是不理解,求告知

    作者回复: 正常情况下,如果一个线程set之后,该线程销毁了,然后key值由于弱引用刚好遇到一次GC,被回收了,此时value已经出现内存泄漏。而threadlocal为了解决这个问题,在后面的线程进行set时,会把之前key值为null的value清空掉,所以就不会出现大量内存泄漏了。 所以我们要模拟的就是,在后面进来的线程set之前,保证之前的线程还没有销毁,之前的key value就会保持,这样我们能模拟出大量value内存泄漏的情况出现。

    2019-08-04
    3
    13
  • 昨夜的柠檬
    实际项目中很多都是这样的,老师正确的写法应该是怎样的?

    作者回复: 正确的写法是在set之后,记得在finally里面remove掉。 try{ localthread.set("test"); }finally{ localthread.remove("test"); }

    2019-10-27
    11
  • CRann
    老师,刚看案例top命令后java的pid是1444,可是为什么后来查线程信息变成top -Hd 1593了?

    作者回复: 截图截错了,自己操作的时候记得输入正确的pid就好了。

    2019-07-31
    10
  • 殿小二
    老师 "而threadlocal为了解决这个问题,在后面的线程进行set时,会把之前key值为null的value清空掉,所以就不会出现大量内存泄漏了。" 后面的线程set的时候也只会在自己持有的ThreadLocalMap上进行操作吧,没有所谓的清空 key为null的value的值吧

    作者回复: 是的,后面线程的set只是在当前线程的ThreadLocalMap上进行操作,不能清空其他线程ThreadLocalMap上已经泄漏的value值。这里指的是同一个线程,ThreadLocal实例没有外部强引用的情况下被回收了,此时key值会被回收,下一次在相同线程下set,value值会被清掉。

    2019-12-03
    6
  • Bruce
    问下老师,jmap和jstack命令能查历史的数据,譬如想查昨天的?

    作者回复: 只能查看运行时的数据,如果需要历史数据,可以在JVM启动参数中加入dump日志参数,启动长时间JVM日志监控: 启动OOM监控日志:-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof 启动GC日志:-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/tmp/heapTest.log

    2020-05-13
    5
  • 偏偏喜欢你
    老是您好最近看到项目有报内存溢出,发现是byte[]的问题,但是在Histogram 下看到排在第一位的是char[]数组,排第二的是byte[] 我是去排查char[]呢还是byte[]

    作者回复: 这两个都是基础数据类型数组,例如char[]是String的基础数据类型,byte[]则是数据传输字节流的基础数据类型,排在第一二是比较常见的,我们需要再看看大小,如果异常大,那就是该基础数据类型之上的某个引用类型的问题。可以通过工具再展开树看看封装基础数据类型的引用类型是什么。

    2019-11-21
    5
收起评论
大纲
固定大纲
常用的监控和诊断内存工具
Linux 命令行工具之 top 命令
Linux 命令行工具之 vmstat 命令
显示
设置
留言
35
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部