内存溢出一直是Java进阶过程中非常重要的知识点,在面试中也经常被问到有没有在现网解决过内存溢出的问题,本文主要总结了几种常见的内存溢出类型和出现相关内存溢出的原因,最后还提供了一些常用的分析与排查内存溢出的工具和方法。

OOM思维导图

内存溢出分类

  • 堆溢出
    1、内存溢出(无法申请到内存)
    jstat查看内存使用情况,检查堆参数,一般是申请的内存太大,比如一个特别大的byte数组
    2、内存泄漏(对象无法回收)
    可以使用MAT工具里面的Path to GC Roots定位,一般是一个特大不能回收的对象

  • 栈溢出
    1、StackOverflowError
    方法调用层次太深,内存不够新建栈帧,比如一个没有结束条件的递归
    2、OutOfMemoryError
    线程太多,内存不够新建线程

  • MetaSpace数据区溢出
    动态生成大量Class

  • 本地直接内存溢出
    申请一个大的直接内存

  • 数据超限内存溢出
    分配的数据结构在此平台不可寻址

排查方法

  • jdk自带命令

    1
    2
    3
    4
    5
    6
    7
    8
    jstat -gcutil 33816 250 20 # 监控jvm的内存使用情况
    jps -ml # 输出虚拟机启动时传递给主类main()的参数,输出主类的全名
    jmap -F -dump:live,format=b,file=eclipse.bin 85962 # dump堆内存
    # 分析方法
    # 可以使用VisualVM,主要查看是否存在大对象,然后分析为什么没有被回收
    jstack -F -l -m 85962 # 显示虚拟机快照
    jstack -l PID >> test.txt
    jmap -histo pid # 实时查看JVM对象大小的排序结果,但无法看到对象的具体内容
  • arthas

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 找到最耗CPU的方法
    thread -n 5
    # 观察方法的返回
    watch com.test.MyClass query returnObj
    # 反编译类
    jad com.test.MyClass
    # 监控调用情况
    monitor -c 5 com.test.MyClass query
    # 查看分段的调用时间
    trace com.test.MyClass query
    # 查看方法完整的调用链
    stack com.test.MyClass query
    # 记录调用现场
    tt -t com.test.MyClass query
  • 参数检查

    1
    2
    3
    4
    5
    -Xms 初始堆大小
    -Xmx 最大堆大小
    -Xmn 新生代大小
    XX:MaxMetaSpaceSize 元数据区大小
    Survivor Eden与Survivor区的大小比值
Contents
  1. 1. 内存溢出分类
  2. 2. 排查方法