常见的内存溢出问题(内存和MetaSpace内存)

常见的内存溢出问题有,内存和metaspace内存

Java堆内存溢出

Java堆内存溢出有两种形式的错误:

  1. OutOfMemoryError: Java heap space
  2. OutOfMemoryError: GC overhead limit exceeded

OutOfMemoryError: Java heap space

在Java堆中不断地创建对象,并且GC-Roots到对象之间存在引用链,这样JVM就不会回收对象。

1
2
3
4
5
6
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
while (true) {
list.add(1);
}
}

OutOfMemoryError: GC overhead limit exceeded

GC overhead limit exceed检查是Hotspot VM 1.6定义的一个策略,通过统计GC时间来预测是否要OOM了,提前抛出异常,防止OOM发生。Sun官方对此的定义是:“并行/并发回收器在GC回收时间过长时会抛出OutOfMemoryError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存。用来避免内存过小造成应用不能正常工作。”

1
2
3
4
5
6
7
8
9
10
public void testOom1() {
List<Map<String, Object>> mapList = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
Map<String, Object> map = new HashMap<>();
for (int j = 0; j < i; j++) {
map.put(String.valueOf(j), j);
}
mapList.add(map);
}
}

MetaSpace元数据内存溢出

JDK 8中将类信息移到了本地堆内存(Native Heap)中,将原有的永久代移动到了本地堆中成为MetaSpace,如果不指定该区域的大小,JVM将会动态地调整。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HeapOOM.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invoke(o, objects);
}
});
enhancer.create();
}
}

分析案例

线上OOM排查步骤:

  1. 使用dmesg命令查看系统日志

    1
    dmesg | grep -E 'kill|oom|out of memory'

    可以查看操作系统启动后的系统日志,这里就是查看跟内存溢出相关联的系统日志。

  2. 启动项目,使用ps命令查看进程

    1
    2
    ps -aux | grep java
    ps -ef | grep java

    查看你的Java进程的进程ID。

  3. 使用top命令

    1
    top

    在结果列表中,会看到%MEM这一列,可以看到你的进程对内存的使用率特别高。

  4. 使用jstat命令

    1
    2
    jstat -gcutil 20886 1000 10
    jstat -gcutil 8968 500 5

    用jstat工具对指定Java进程(20886是进程ID),按照指定间隔查看统计信息。

    看到的东西类似下面那样:

    1
    2
    S0 S1 E O YGC FGC
    26.80 0.00 10.50 89.90 86 954
  5. 使用jmap命令查看

    1
    jmap -histo pid

    打印出当前堆中所有每个类的实例数量和内存占用。

    把当前堆内存的快照转储到dumpfile_jmap.hprof文件中,然后可以对内存快照进行分析。

    1
    jmap -dump:format=b,file=文件名 [pid]

    例如:

    1
    jmap -dump:format=b,file=D:/log/jvm/dumpfile_jmap.hprof 20886

    接着可以用MAT工具,或者是Eclipse MAT的内存分析插件,来对hprof文件进行分析。