文章目录

  • 问题重现
  • 排查思路
  • 优化方法
  • 知识小结

问题重现

前段时间给DSP(实际竞价广告投放系统)系统开发了一个前置数据去重处理服务,开始两天没有问题,但是第三天去看数据,发现进程不在。

查看阿里云机器负载,发现前两天内存持续在高位,初步怀疑内存溢出,导致异常退出。

排查思路

  • 1.确定代码中有大量对象占用的地方,着重排查:因为堆内存是JVM中内存占用最大的一块。

  • 2.多线程代码排查:多线程下容易出现隐蔽的bug

  • 3.是否是机器本身内存太小,有其他进程占用大量内存。

关于思路1:要清楚使用的jdk版本,以及相应版本对应的JVM内存的分布情况。

要清楚以下几点:

  • 首先JVM内存受限于实际的最大物理内存,JVM内存的最大值跟操作系统有很大的关系。简单的说就32位处理器虽然可控内存空间有4GB,但是具体到JVM操作系统会给一个限制,这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系 统下为2G-3G),而64bit以上的处理器就不会有限制了。

  • JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的内存由-Xmx指定,

    默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;

    空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、 -Xmx相等以避免在每次GC后调整堆的大小。

  • JDK8后,方法区在元空间实现,元空间使用直接内存. 但是一般方法区占用内存并不会太大。

优化方法

如果进程已经挂掉,可以分析堆内存快照,具体设置方式参考下文。如果进程还在,可以查看当前的内存情况,排查问题:

  • 通过java自带的工具查看堆内存情况

    #查看堆内存使用情况
    jmap -heap pidHeap Usage:
    PS Young Generation
    Eden Space:capacity = 290455552 (277.0MB)used     = 290455552 (277.0MB)free     = 0 (0.0MB)100.0% used
    From Space:capacity = 20447232 (19.5MB)used     = 14548992 (13.875MB)free     = 5898240 (5.625MB)71.15384615384616% used
    To Space:capacity = 20447232 (19.5MB)used     = 0 (0.0MB)free     = 20447232 (19.5MB)0.0% used
    PS Old Generationcapacity = 420478976 (401.0MB)used     = 364028688 (347.16481018066406MB)free     = 56450288 (53.83518981933594MB)86.5747656310883% used
    

    这里需要了解JVM垃圾回收器的分代逻辑,简单来说就是新创建的对象放在Eden Space, 之后将存活的对象放入From Space和To Space区域,如果超过一定次数,则放入Old Generation,所以如果老年代如果空间一直上升,而没有下降,则说明有内存泄露,导致对象一直没有释放,就要重点排查。具体可以结合dump文件。

  • 通过java自带工具jmap查看class实例等信息,作用类似于查看dump文件。

    #输出当前class的实例数目、内存占用、类全名信息。
    [root@iZ0xicvgqrspr6w1sxojjeZ tmp]# jmap -histo:live 6104 | head -n 20num     #instances         #bytes  class name
    ----------------------------------------------1:         66065       10737048  [C2:         12932        9308496  [B3:         12049        4969152  [I4:         28079        2562408  [Ljava.lang.Object;5:         65697        1576728  java.lang.String6:         48265        1544480  java.util.concurrent.ConcurrentHashMap$Node7:         39782        1273024  java.util.HashMap$Node8:         11210        1239576  java.lang.Class9:          1036        1064000  [Lio.netty.util.Recycler$DefaultHandle;10:         15295         611800  java.security.cert.TrustAnchor11:          5817         511896  java.lang.reflect.Method12:          1602         493712  [Ljava.util.concurrent.ConcurrentHashMap$Node;13:          3865         485632  [Ljava.util.HashMap$Node;14:         20168         484032  java.util.ArrayList15:         27529         440464  java.lang.Object16:          8681         347240  java.util.LinkedHashMap$Entry17:           390         255840  io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueue
    
  • 通过jstat命令查看gc情况

    如果FGC次数比较频繁,则说明内存吃紧,要排查是内存本身较小,还是内存泄露。

    [root@iZ0xicvgqrspr6w1sxojjeZ work]# jstat -gc 28686S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
    8192.0 7680.0 4544.0  0.0   770560.0 497923.9 1310720.0   852555.0  65624.0 62895.9 8024.0 7467.0 146565 2120.377  132    84.347 2204.724
    

    S0C:年轻代中第一个幸存区的大小

    S1C:年轻代中第二个幸存区的大小

    S0U:年轻代中第一个幸存区的使用大小

    S1U:年轻代中第二个幸存区的使用大小

    EC:年轻代中伊甸园区的大小

    EU:年轻代中伊甸园区的使用大小

    OC:老年代大小

    OU:老年代使用大小

    MC:方法区大小

    MU:方法区使用大小

    CCSC:压缩类空间大小

    CCSU:压缩类空间使用大小

    YGC:年轻代gc次数

    YGCT:年轻代消耗时间

    FGC:老年代gc次数

    FGCT:老年代gc消耗时间

    GCT:gc消耗总时间

  • 查看内存占用靠前的java进程

    top -o %MEM -b -n 1 | grep java | awk '{print "PID: "$1" \t MEM: "$6" \t %CPU: "$9"% \t %MEM: "$10"%"}'
    
  • 到处堆内存快照

    jmap -histo [进程id] > jmap.txt
    
  • 首先调整java运行时的相关参数,包括内存溢出时的堆内存快照。

    nohup java -Xmx2048m -Xms2048m -Xmn768m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -jar ${jar_name} --spring.profiles.active=prod-us --server.port=8089 >/dev/null 2>&1 &
    
    JVM参数 描述 默认 推荐
    -Xms Java堆内存的最小值,即初始值 OS内存1/64 OS内存一半
    -Xmx java堆内存最大值 OS内存1/4 OS内存一半
    -Xmn 堆内存新生代大小,扣除新生代
    就是老年代大小
    默认堆的1/3 sun推荐3/8
    -Xss 每个线程的栈内存大小 和jdk有关 512k~1M

    如果是本身内存设置过小,一般通过调整参数便可以解决。如果是其他问题,可以通过heapdump.hprof文件协助查看占用内存较大的对象分布,方便排查。

    根据这些信息,重点排查相关部位的代码,一般都能解决。之前我还遇到在使用线程池时,采用了Executors工具类去创建线程池,而用它创建线程池,默认的等待队列是无界队列,所以有可能导致创建大量等待线程,导致内存溢出(线程要占用资源)。正确的做法是使用下面这种方式,给一个有界队列作为参数。

    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS,new LinkedBlockingQueue<>(2000), new DefaultThreadFactoryA(), new ThreadPoolExecutor.CallerRunsPolicy());
    

  • 查看java进程常用命令:

    #1.简洁信息:可以查看进程号
    jps
    #2.可查看进程号和jar包名称
    jcmd
    jps -lv
    #3.详细信息
    ps -ef|grep java
    
  • 查看系统内存情况:

    #Top命令 ,可以查看系统负载情况,内存占用,和进程的情况
    top - 18:59:23 up 5 days,  8:28,  2 users,  load average: 28.50, 30.68, 30.82
    Tasks:  84 total,   1 running,  83 sleeping,   0 stopped,   0 zombie
    %Cpu(s):  0.3 us,  0.3 sy,  0.0 ni, 99.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    KiB Mem :  8008928 total,  3534996 free,  3400088 used,  1073844 buff/cache
    KiB Swap:        0 total,        0 free,        0 used.  4355048 avail Mem PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND            1150 root      10 -10  140104  18748  11464 S   1.3  0.2  90:48.01 AliYunDun          947 root      20   0 1354476  21268   8416 S   0.3  0.3  29:38.53 /usr/local/clou      1 root      20   0   43584   3976   2620 S   0.0  0.0   0:04.72 systemd                 2 root      20   0       0      0      0 S   0.0  0.0   0:00.00 kthreadd               4 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H           5 root      20   0       0      0      0 S   0.0  0.0   0:00.00 kworker/u4:0           6 root      20   0       0      0      0 S   0.0  0.0   0:35.24 ksoftirqd/0           7 root      rt   0       0      0      0 S   0.0  0.0   0:00.44 migration/0           8 root      20   0       0      0      0 S   0.0  0.0   0:00.00 rcu_bh                 9 root      20   0       0      0      0 S   0.0  0.0   1:27.46 rcu_sched
    

    只想查看java进程的情况:

    [root@iZ0xicvgqrspr6w1sxojjeZ ~]# top -o %MEM -b -n 1 | grep java | awk '{print "PID: "$1" \t MEM: "$6" \t %CPU: "$9"% \t %MEM: "$10"%"}'
    PID: 1592        MEM: 2.4g       %CPU: 0.0%      %MEM: 31.4%
    PID: 1506        MEM: 383856     %CPU: 0.0%      %MEM: 4.8%
    

知识小结

内存泄露、JVM调优等问题一般比较隐蔽,首先要对jvm的相关知识有一个大体认知,其次要熟悉java中相关工具类的正确用法,最后就是要有给力的工具,比如jprofiler等等。

一次内存泄露排查小结相关推荐

  1. Netty堆外内存泄露排查与总结

    导读 Netty 是一个异步事件驱动的网络通信层框架,用于快速开发高可用高性能的服务端网络框架与客户端程序,它极大地简化了 TCP 和 UDP 套接字服务器等网络编程. Netty 底层基于 JDK ...

  2. Netty堆外内存泄露排查盛宴

    导读 Netty 是一个异步事件驱动的网络通信层框架,用于快速开发高可用高性能的服务端网络框架与客户端程序,它极大地简化了 TCP 和 UDP 套接字服务器等网络编程. Netty 底层基于 JDK ...

  3. Netty堆外内存泄露排查与总结 1

    导读 Netty 是一个异步事件驱动的网络通信层框架,用于快速开发高可用高性能的服务端网络框架与客户端程序,它极大地简化了 TCP 和 UDP 套接字服务器等网络编程. Netty 底层基于 JDK ...

  4. java 内存泄露 书籍_[Java教程]一次艰难的内存泄露排查,BeanUtils 的锅

    [Java教程]一次艰难的内存泄露排查,BeanUtils 的锅 0 2020-10-29 18:24:42 现象 通过jstat -gcutil pid 5000 ,发现fgc次数很多而且频繁,此时 ...

  5. Java —— 内存泄露排查

    最近发现服务器内存使用持续增长且增长速率大,怀疑是内存泄露导致的. 最终定位到是因为程序中存在线程池频繁创建但未销毁问题导致线程泄露,进而影响内存使用增长. Tips:本文不记录排错过程,只记录可疑点 ...

  6. 内存泄露排查之线程泄露

    作者:mrguozp https://www.cnblogs.com/guozp/p/10597327.html 基础 内存泄露(Memory Leak) java中内存都是由jvm管理,垃圾回收由g ...

  7. 极光推送SDK引起的内存泄露排查

    发现问题 发现推送服务的老年代不断增长,部分内存无法回收 内存泄露堆栈分析 通过运维平台ark,执行了jmap进行heaphump,使用mat工具分析,发现可能存在内存泄露 发现有大量的SocksSo ...

  8. java 堆外内存泄露排查

    参考:http://blog.itpub.net/70016482/viewspace-2908649/ 一.原理 JDK提供绕过JVM直接在操作系统申请内存的接口,例如通过Unsafe类的alloc ...

  9. Linux下内存泄露排查讨论

    作为c的程序员,最常见的就是排查内存泄漏,不过我们一般的内存泄漏是针对特定的程序去排查,相对来说比较容易,但是如果是维护人员,不知道哪个程序有内存泄漏,甚至是应用程序的内存泄漏,还是内核的内存泄漏都不 ...

最新文章

  1. live2dmesh渲染优先级_live2dsdk的opengl示例详解
  2. 敏捷个人手机应用:如何使用时中法习惯
  3. java轻松实现无锁队列
  4. Android Audio代码分析2 - 函数getMinBufferSize
  5. BZOJ1050 [HAOI2006]旅行comf
  6. 什么时候使用resulttype_柳州站东广场到底什么时候可以使用?
  7. python 获取excel文本框_简单使用python做excel多文件批量搜索(带图形界面)(已更新)...
  8. Python机器学习简介
  9. plsql 通过 excel 创建表
  10. 三分钟了解阿里云和腾讯云的DDoS防御策略
  11. 重复抽样与不重复抽样的抽样平均误差大小?
  12. 天翼云对象存储Java对接(经典版 Ⅱ型)
  13. python实现数字规律_从学习python总结语言规律
  14. 小猿圈之初识python基础知识
  15. 计算机双语论文,计算机双语教学初探论文
  16. 经典案例--JS购物车
  17. 我可以在iPad上使用WhatsApp吗?
  18. 开源分布式存储系统的对比
  19. python学习之文件
  20. 企鹅号绑定微信公众号 问题 微信授权失败!输入的微信号和微信公众平台设置的不一致

热门文章

  1. jitter单位_时间抖动(jitter)的概念及其分析方法
  2. ALPEN阿尔卑斯山时间管理法
  3. 散文说python半篇——景观三元论与盖茨比的对话
  4. 接班人不是克隆出来的:华为再显接班难
  5. 高屋建瓴-------谈观看朱老师视频有感
  6. 电脑调整分区后分区不见恢复数据的方法
  7. 2022最新200道软件测试面试题
  8. 修改Chromium源码实现HEVC/H.265 4K视频播放
  9. 【美化§炫彩篮球火win7电脑主题】
  10. 数独挑战之九宫格入门第一题解题思路