Java性能调优实战
刘超
金山软件西山居技术经理
立即订阅
7535 人已学习
课程目录
已完结 48 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 怎样才能做好性能调优?
免费
模块一 · 概述 (2讲)
01 | 如何制定性能调优标准?
02 | 如何制定性能调优策略?
模块二 · Java编程性能调优 (10讲)
03 | 字符串性能优化不容小觑,百M内存轻松存储几十G数据
04 | 慎重使用正则表达式
05 | ArrayList还是LinkedList?使用不当性能差千倍
加餐 | 推荐几款常用的性能测试工具
06 | Stream如何提高遍历集合效率?
07 | 深入浅出HashMap的设计与优化
08 | 网络通信优化之I/O模型:如何解决高并发下I/O瓶颈?
09 | 网络通信优化之序列化:避免使用Java序列化
10 | 网络通信优化之通信协议:如何优化RPC网络通信?
11 | 答疑课堂:深入了解NIO的优化实现原理
模块三 · 多线程性能调优 (10讲)
12 | 多线程之锁优化(上):深入了解Synchronized同步锁的优化方法
13 | 多线程之锁优化(中):深入了解Lock同步锁的优化方法
14 | 多线程之锁优化(下):使用乐观锁优化并行操作
15 | 多线程调优(上):哪些操作导致了上下文切换?
16 | 多线程调优(下):如何优化多线程上下文切换?
17 | 并发容器的使用:识别不同场景下最优容器
18 | 如何设置线程池大小?
19 | 如何用协程来优化多线程业务?
20 | 答疑课堂:模块三热点问题解答
加餐 | 什么是数据的强、弱一致性?
模块四 · JVM性能监测及调优 (6讲)
21 | 磨刀不误砍柴工:欲知JVM调优先了解JVM内存模型
22 | 深入JVM即时编译器JIT,优化Java编译
23 | 如何优化垃圾回收机制?
24 | 如何优化JVM内存分配?
25 | 内存持续上升,我该如何排查问题?
26 | 答疑课堂:模块四热点问题解答
模块五 · 设计模式调优 (6讲)
27 | 单例模式:如何创建单一对象优化系统性能?
28 | 原型模式与享元模式:提升系统性能的利器
29 | 如何使用设计模式优化并发编程?
30 | 生产者消费者模式:电商库存设计优化
31 | 装饰器模式:如何优化电商系统中复杂的商品价格策略?
32 | 答疑课堂:模块五思考题集锦
模块六 · 数据库性能调优 (8讲)
33 | MySQL调优之SQL语句:如何写出高性能SQL语句?
34 | MySQL调优之事务:高并发场景下的数据库事务调优
35 | MySQL调优之索引:索引的失效与优化
36 | 记一次线上SQL死锁事故:如何避免死锁?
37 | 什么时候需要分表分库?
38 | 电商系统表设计优化案例分析
39 | 数据库参数设置优化,失之毫厘差之千里
40 | 答疑课堂:MySQL中InnoDB的知识点串讲
模块七 · 实战演练场 (4讲)
41 | 如何设计更优的分布式锁?
42 | 电商系统的分布式事务调优
43 | 如何使用缓存优化系统性能?
44 | 记一次双十一抢购性能瓶颈调优
结束语 (1讲)
结束语 | 栉风沐雨,砥砺前行!
Java性能调优实战
登录|注册

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

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

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

工欲善其事,必先利其器。平时排查内存性能瓶颈时,我们往往需要用到一些 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/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Java性能调优实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(23)

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

    https://mp.weixin.qq.com/s/IPi3xiordGh-zcSSRie6nA

    作者回复: 赞!

    2019-07-18
    4
    24
  • 我已经设置了昵称
    老师是否可以讲下如何避免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
    5
    20
  • WL
    请问一下老师内存泄露和内存溢出具体有啥区别,有点不太理解内存泄露的概念。

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

    内存溢出则是发生了OutOfMemoryException,内存溢出的情况有很多,例如堆内存空间不足,栈空间不足,以及方法区空间不足都会发生内存溢出异常。

    内存泄漏与内存溢出的关系:内存泄漏很容易导致内存溢出,但内存溢出不一定是内存泄漏导致的。

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

    作者回复: 👍

    2019-09-22
    5
  • 刘天若Warner
    老师,为什么线程要sleep一下,看了注释还是不理解,求告知

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

    所以我们要模拟的就是,在后面进来的线程set之前,保证之前的线程还没有销毁,之前的key value就会保持,这样我们能模拟出大量value内存泄漏的情况出现。

    2019-08-04
    3
  • 张衡
    老师您好,看了您的课程收货颇丰!谢谢
    现在有个问题想咨询下

    前提:jdk7u24 xms8g xmx8g g1垃圾回收
    现象:
    堆内存使用量从2G一直到6.3G都没有young gc 和 full gc

    当堆内存使用量到了7G的时候直接进行了full gc
    并且周期性重复上面的full gc
    查看GC日志 eden区回收前高达6.3G

    请问老师。现在该如何调优呢

    作者回复: 如果没有设置年轻代与老年代的比例,默认分配给年轻代最大比例为60%,而且默认会先触发young gc,所以你说的这种情况比较少见,检查是否长时间存活的对象太多导致的。

    这种情况优化设置参数已经没有很明显的作用了,建议先查找内存爆满的原因。

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

    作者回复: 正确的写法是在set之后,记得在finally里面remove掉。

    try{
      localthread.set("test");
    }finally{
       localthread.remove("test");
    }

    2019-10-27
    1
  • JackJin
    我用ab测试,设置请求数量一万,请求test0,内存就溢出,;还没请求到test1,?

    作者回复: 内存泄露导致有大量对象无法回收,占满了堆内存情况下,就会导致内存溢出。我在这里加了一个test1只是为了创建更多的对象,从而更容易发生内存溢出。

    2019-07-18
    1
  • Liam
    能否讲下这个测试用例是怎么设计的,为什么跑1w次AB两个方法,在1G的堆内存下会发生OOM

    作者回复: 平时仅仅某些内存泄漏,一般不会导致内存溢出。
    所以在这里,test0请求方法中ThreadLocal为内存泄漏,而test1是一个触发内存溢出的条件,小请求量时没有问题,当请求量比较大时,就出现内存溢出情况了。

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

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

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

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

    2019-11-21
  • Demon.Lee
    final static ThreadLocal<Byte[]> localVariable = new ThreadLocal<Byte[]>();
    -----------
    老师,内存泄露是因为这个localVariable在下一次GC时被回收了么? 但它不是final static修饰的么?不是不会变化的么

    作者回复: 没有更新最新代码,现在已更新上去,localVariable 是一个局部变量

    2019-10-20
  • godtrue
    老师请问OS是怎么监控每个进程的资源使用情况的?直接在内存中收集到相关的数据?
    其他监控工具的工作原理是是什么?是借助OS的API获取相关监控信息还是自己有做了什么拿到了监控进程的运行数据?
    2019-09-12
  • Charlie
    没看到有test1啊。。。

    作者回复: 之前的代码已经优化了,所以去掉了test1,重写写了test0方法,两个方法对于大家来说不是很好理解

    2019-08-31
  • WolvesLeader
    java -jar -Xms1000m -Xmx4000m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -Xms1g -Xmx1g -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/tmp/heapTest.log heapTest-0.0.1-SNAPSHOT.jar
    配置了2个-Xms和-Xmx,为啥要配置2个

    作者回复: 一个就够了,已修正

    2019-08-21
  • 星星滴蓝天
    代码中对jvm监控常用方法是啥?我翻了翻留言,没有人问这个问题的

    作者回复: 可以通过ManagementFactory中的RuntimeMXBean实时获取JVM对应的值

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

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

    2019-07-31
  • 我已经设置了昵称
    同理不明白为什么这里test0,test1方法会内存泄漏,请老师自己讲下

    作者回复: tesr0是内存泄露,test1则是正常的分配堆内存,这里test1只是模拟在内存溢出的情况下,如果有大量对象创建的情况下,很容易导致内存溢出。

    2019-07-18
    1
  • QQ怪
    学到了很多,感谢感谢
    2019-07-18
  • 撒旦的堕落
    老师 这段代码有点不解的地方 test0使用线程池 所以线程一直存活 而每个线程的threadlocalmap中 含有4m的内存 没法释放 100个线程 才400m被占用 而test1方法使用的是局部变量 方法执行后内存就会被回收 4g的内存为啥就溢出了

    作者回复: 如果发生GC的情况下,threadlocalmap产生的占用内存对象就不止400m,也就是说发生内存溢出的情况下。

    2019-07-18
收起评论
23
返回
顶部