JVM调优-OutOfMemoryError 分析

JVM调优-OutOfMemoryError 分析本文已参与「新人创作礼」活动,一起开启掘金创作之路。 JVM OutOfMemoryError 分析 1.Java 堆溢出 1.1 设置JVM参数 -Xmx20m:设置JVM最大可用内存为20M。 –

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

JVM OutOfMemoryError 分析

1.Java 堆溢出

1.1 设置JVM参数
-verbose:gc -Xms20M -Xmx20M -Xmn10M  -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:SurvivorRatio=8
  1. -Xmx20m:设置JVM最大可用内存为20M。
  2. -Xms20m:设置JVM促使内存为20m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内
  3. -Xmn10m:设置年轻代大小为10m
  4. -XX:SurvivorRatio=8 Eden区和Survivor区间比例是8:1, 所以分配一下就是 eden8m, fromspace 1m, to space1m 加起来10m
1.2 测试代码
package com.jzj.jvmtest.oomtest;

import java.util.ArrayList;
import java.util.List;

/** * 堆溢出, 只要不停的新建对象,就会堆溢出 */
public class HeapOOM {
    public static void main(String[] args) {
        System.out.print("最大内存: ");
        System.out.println(Runtime.getRuntime().maxMemory() / 1024 / 1024 + "MB");
        System.out.print("可用内存: ");
        System.out.println(Runtime.getRuntime().freeMemory() / 1024 / 1024 + "MB");
        System.out.print("已使用内存: ");
        System.out.println(Runtime.getRuntime().totalMemory() / 1024 / 1024 + "MB");

        List<HeapOOM> list = new ArrayList<>();
        while (true) {
            list.add(new HeapOOM());
        }
    }
}

1.3 运行OOM日志

可以看到 日志 java.lang.OutOfMemoryError: Java heap space Heap Space 就是堆溢出

最大内存: 19MB
可用内存: 16MB
已使用内存: 19MB
[GC (Allocation Failure) [PSYoungGen: 8192K->1000K(9216K)] 8192K->4533K(19456K), 0.0044438 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) --[PSYoungGen: 9192K->9192K(9216K)] 12725K->19427K(19456K), 0.0091278 secs] [Times: user=0.11 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) [PSYoungGen: 9192K->0K(9216K)] [ParOldGen: 10235K->10221K(10240K)] 19427K->10221K(19456K), [Metaspace: 3495K->3495K(1056768K)], 0.1549254 secs] [Times: user=0.69 sys=0.02, real=0.16 secs] 
[Full GC (Ergonomics) [PSYoungGen: 8192K->7167K(9216K)] [ParOldGen: 10221K->8971K(10240K)] 18413K->16138K(19456K), [Metaspace: 3496K->3496K(1056768K)], 0.1722236 secs] [Times: user=1.30 sys=0.00, real=0.17 secs] 
[Full GC (Ergonomics) [PSYoungGen: 7652K->7604K(9216K)] [ParOldGen: 8971K->8971K(10240K)] 16624K->16575K(19456K), [Metaspace: 3496K->3496K(1056768K)], 0.1296002 secs] [Times: user=1.25 sys=0.00, real=0.13 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 7604K->7604K(9216K)] [ParOldGen: 8971K->8953K(10240K)] 16575K->16557K(19456K), [Metaspace: 3496K->3496K(1056768K)], 0.1372692 secs] [Times: user=1.27 sys=0.00, real=0.14 secs] 
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid28084.hprof ...
Heap dump file created [28363283 bytes in 0.058 secs]
Heap
 PSYoungGen      total 9216K, used 7794K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 95% used [0x00000000ff600000,0x00000000ffd9c8a8,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 10240K, used 8953K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 87% used [0x00000000fec00000,0x00000000ff4be5a0,0x00000000ff600000)
 Metaspace       used 3527K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 385K, capacity 388K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:3210)
	at java.util.Arrays.copyOf(Arrays.java:3181)
	at java.util.ArrayList.grow(ArrayList.java:265)
	at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
	at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
	at java.util.ArrayList.add(ArrayList.java:462)
	at com.jzj.jvmtest.oomtest.HeapOOM.main(HeapOOM.java:20)

Process finished with exit code 1

2.Java栈、本地方法栈溢出

2.1 设置JVM参数
-verbose:gc -Xms20M -Xmx20M -Xmn10M  -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:SurvivorRatio=8  -Xss128k

这次最后加了一个-Xss 设置每个线程的堆栈大小

  1. 虚拟机扩展栈时,如果栈深度大于虚拟机允许最大深度,就会出现StackOverflowError异常
  2. 如果在扩展栈时,需要的内存空间不够,那么就会出现OutOfMemoryError 就是OOM异常
2.2 测试代码
package com.jzj.jvmtest.oomtest;

/** * 栈溢出, 只要不停的新建对象,循环递归,超出栈容量限制就会OOM * * 1.栈深入超出限制,stackOverflowError * 2.栈扩展时候,内存不够,导致OOM异常 */
public class StackOOM {

    /** * 初始化栈的深度 */
    private int stackLength = 1;

    /** * 循环去增加栈的深度 */
    public void stackLeak() {
        stackLength++;
        stackLeak();
    }

    public static void main(String[] args) {
        System.out.print("最大内存: ");
        System.out.println(Runtime.getRuntime().maxMemory() / 1024 / 1024 + "MB");
        System.out.print("可用内存: ");
        System.out.println(Runtime.getRuntime().freeMemory() / 1024 / 1024 + "MB");
        System.out.print("已使用内存: ");
        System.out.println(Runtime.getRuntime().totalMemory() / 1024 / 1024 + "MB");

        StackOOM oom = new StackOOM();

        try {
            oom.stackLeak();

        } catch (Throwable e) {
            System.out.println("stack length ============" + oom.stackLength);
            throw e;
        }
    }
}

2.3 运行OOM日志

可以看到 日志Exception in thread “main” java.lang.StackOverflowError StackOverflowError 就是虚拟机栈 ,可以看到栈深度在995的时候就发生了异常,超出了虚拟机栈的深度,抛出异常

最大内存: 19MB
可用内存: 16MB
已使用内存: 19MB
stack length ============995
Heap
 PSYoungGen      total 9216K, used 4158K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 50% used [0x00000000ff600000,0x00000000ffa0fae8,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
 ParOldGen       total 10240K, used 0K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff600000)
 Metaspace       used 3558K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 385K, capacity 388K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.StackOverflowError
	at com.jzj.jvmtest.oomtest.StackOOM.stackLeak(StackOOM.java:19)
	at com.jzj.jvmtest.oomtest.StackOOM.stackLeak(StackOOM.java:20)
	at com.jzj.jvmtest.oomtest.StackOOM.stackLeak(StackOOM.java:20)
	at com.jzj.jvmtest.oomtest.StackOOM.stackLeak(StackOOM.java:20)
    at com.jzj.jvmtest.oomtest.StackOOM.main(StackOOM.java:34)

Process finished with exit code 1
2.4 Java虚拟机OOM异常

理论上要想虚拟机栈抛出OOM,在多线程的情况下,可以不停的新建线程来实现OOM

但是这样 通过多个线程创建的OOM异常和栈空间是否足够大没有直接关系? 准确来说为每个线程栈分配的空间越大,反而越容易长身OOM why? 为什么 为每个线程分配的空间越大,越容易OOM呢?

分析:

  1. 假设操作系统有2G内存
  2. 虚拟机可以通过参数来控制虚拟机堆 和 方法区允许内存的最大值
  3. 堆最大值 -Xmx 控制,方法区永久区 -MaxPermSize最大方法区容量控制方法区大小
  4. 2GB 减去 堆大小, 减去 方法区大小, 剩下的内存就由 java 虚拟机栈与本地方法栈共享瓜分
  5. 每个线程分配的空间越大, 那么系统可以操作的线程数就越少,那么新建线程就越容易超标,造成OOM
!!!!!!!! 为什么在这里我不代码演示一下创建线程导致oom

因为Java的线程时映射到操作系统内核线程上的,你创建多的线程,执行这种代码,容易造成操作系统假死,电脑死机,所以谨慎操作,这里就不演示这种OOM了

3.Java 运行常量池溢出

3.1 设置JVM参数-注意区分jdk版本
-verbose:gc -Xms20M -Xmx20M -Xmn10M  -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:SurvivorRatio=8 -XX:PermSize=5M -XX:MaxPermSize=5M

这里新加了参数 -XX:PermSize=5M -XX:MaxPermSize=5M 来控制方法区大小

  1. -XX:PermSize=5M:设置方法区大小。1.6JDK
  2. -XX:MaxPermSize=5M:设置方法区最大值。1.6JDK

!!!!!! 注意 这个测试方法用的是 String.valueOf(i++).intern() 这个方法 在jdk 1.6没问题, 是能够抛出 PermSpace OOM的 因为

  1. 在 JDK 1.6 及之前的版本中,由于常量池分配在永久代内,我们可以通过 -XX:PermSize 和 -XX:MaxPermSize 限制方法区大小,从而间接限制其中常量池的容量
  2. 但是 在 JDK1.8运行这段程序不会得到相同的结果,而是出现以下的提示信息,这是因为这两个参数已经不在JDK1.7中使用了。
  3. 提示错误 Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=5M; support was removed in 8.0
  4. 提示错误 Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=5M; support was removed in 8.0

不同的版本 ,这段代码抛出不同的异常

  • VM Args(jdk1.6): -XX:PermSize=10M -XX:MaxPermSize=10M

  • VM Args(jdk1.7): -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError

  • VM Args (jdk1.8) : -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m

  • Exception(jdk1.6) : java.lang.OutOfMemoryError: PermGen space

  • Exception(jdk1.7): java.lang.OutOfMemoryError: Java heap space

  • Exception(jdk1.8): java.lang.OutOfMemoryError: GC overhead limit exceeded

3.2 测试代码

所以 为了在1.8的jdk中 ,测试常量池溢出 ,我们该如何模拟呢? jdk1.8 使用元空间( Metaspace )替代了永久代( PermSize ),因此我们可以在 1.8 中指定 Metaspace 的大小模拟 测试

!!!! JDK1.8这里新加了参数 来控制方法区大小

  1. -XX:MetaspaceSize=5m,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,
  2. -XX:MaxMetaspaceSize=5m,最大空间,默认是没有限制的。
/** * 设置方法区及常量池 OOM溢出 * 方法区中运行时常量池溢出 * VM Args(jdk1.6): -XX:PermSize=10M -XX:MaxPermSize=10M * VM Args(jdk1.7): -Xms30m -Xmx30m -XX:+HeapDumpOnOutOfMemoryError * VM Args (jdk1.8) : -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m * Exception(jdk1.6) : java.lang.OutOfMemoryError: PermGen space * Exception(jdk1.7): java.lang.OutOfMemoryError: Java heap space * Exception(jdk1.8): java.lang.OutOfMemoryError: GC overhead limit exceeded */
public class ConstantOOM {
    public static void main(String[] args) {
        System.out.print("最大内存: ");
        System.out.println(Runtime.getRuntime().maxMemory() / 1024 / 1024 + "MB");
        System.out.print("可用内存: ");
        System.out.println(Runtime.getRuntime().freeMemory() / 1024 / 1024 + "MB");
        System.out.print("已使用内存: ");
        System.out.println(Runtime.getRuntime().totalMemory() / 1024 / 1024 + "MB");


        //使用List ,保持常量池的使用, 避免FullGC回收常量池的行为
        List<String> staticList = new ArrayList<>();

        //5M的PermSize 让List for循环添加变量 让常量池疯狂的添加新的对象
        long i = 0L;
        while (true) {
            //使用 String.intern 调用”ab”.intern()方法的时候会返回”ab“
            // 但是这个方法会首先检查字符串池中是否有”ab”这个字符串,如果存在则返回这个字符串的引用
            // 否则就将这个字符串添加到字符串池中,然会返回这个字符串的引用
            staticList.add(String.valueOf(i++).intern());
        }
    }
}
3.3 运行OOM日志

可以看到 日志 Exception in thread “main” java.lang.OutOfMemoryError: GC overhead limit exceeded 并不是 MetaSpace OOM的日志, 所以 JDK1.8中 ,上面的 代码是没法实现 元空间的内存溢出的

最大内存: 19MB
可用内存: 16MB
已使用内存: 19MB
[GC (Allocation Failure) [PSYoungGen: 8192K->1016K(9216K)] 8192K->5564K(19456K), 0.0041671 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) --[PSYoungGen: 9208K->9208K(9216K)] 13756K->19446K(19456K), 0.0086760 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) [PSYoungGen: 9208K->2482K(9216K)] [ParOldGen: 10237K->10179K(10240K)] 19446K->12662K(19456K), [Metaspace: 3497K->3497K(1056768K)], 0.1127624 secs] [Times: user=0.41 sys=0.00, real=0.11 secs] 
[Full GC (Ergonomics) [PSYoungGen: 8192K->7167K(9216K)] [ParOldGen: 10179K->10165K(10240K)] 18371K->17332K(19456K), [Metaspace: 3498K->3498K(1056768K)], 0.1072663 secs] [Times: user=0.63 sys=0.00, real=0.11 secs] 
......
[Full GC (Ergonomics) [PSYoungGen: 8192K->8192K(9216K)] [ParOldGen: 10199K->10199K(10240K)] 18391K->18391K(19456K), [Metaspace: 3502K->3502K(1056768K)], 0.0641831 secs] [Times: user=0.63 sys=0.00, real=0.06 secs] 
[Full GC (Ergonomics) [PSYoungGen: 8192K->8192K(9216K)] [ParOldGen: 10201K->10201K(10240K)] 18393K->18393K(19456K), [Metaspace: 3502K->3502K(1056768K)], 0.0631814 secs] [Times: user=0.48 sys=0.00, real=0.06 secs] 
[Full GC (Ergonomics) [PSYoungGen: 8192K->8192K(9216K)] [ParOldGen: 10202K->10202K(10240K)] 18394K->18394K(19456K), [Metaspace: 3502K->3502K(1056768K)], 0.0626523 secs] [Times: user=0.48 sys=0.00, real=0.06 secs] 
java.lang.OutOfMemoryError: GC overhead limit exceeded
Dumping heap to java_pid10140.hprof ...
Heap dump file created [24293672 bytes in 0.065 secs]
[Full GC (Ergonomics) [PSYoungGen: 8192K->0K(9216K)] [ParOldGen: 10238K->763K(10240K)] 18430K->763K(19456K), [Metaspace: 3522K->3522K(1056768K)], 0.0068815 secs] [Times: user=0.16 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 9216K, used 273K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 3% used [0x00000000ff600000,0x00000000ff6444e0,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 10240K, used 763K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 7% used [0x00000000fec00000,0x00000000fecbeca8,0x00000000ff600000)
 Metaspace       used 3616K, capacity 4540K, committed 4864K, reserved 1056768K
  class space    used 394K, capacity 428K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
	at java.lang.Integer.toString(Integer.java:403)
	at java.lang.String.valueOf(String.java:3099)
	at com.jzj.jvmtest.oomtest.PermOOM.main(PermOOM.java:28)
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=5M; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=5M; support was removed in 8.0

Process finished with exit code 1

4.Java 方法区溢出-jdk8

4.1 设置JVM参数

所以 为了在1.8的jdk中 ,测试方法区溢出 ,我们该如何模拟呢? jdk1.8 使用元空间( Metaspace )替代了永久代( PermSize ),因此我们可以在 1.8 中指定 Metaspace 的大小模拟 测试,我们用

!!!! JDK1.8这里新加了参数 来控制方法区大小

  1. -XX:MetaspaceSize=5m,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,
  2. -XX:MaxMetaspaceSize=5m,最大空间,默认是没有限制的。
-verbose:gc -Xms20M -Xmx20M -Xmn10M  -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m

这里新加了参数 -XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M 来控制方法区大小

  1. -XX:MetaspaceSize=10m,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,
  2. -XX:MaxMetaspaceSize=10m,最大空间,默认是没有限制的。

方法区存的什么? 类信息,常量,静态变量,我们就用CGLib 代理来创建类,使得方法区内存溢出

4.2 测试代码
package com.jzj.jvmtest.oomtest;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;

/** * 方法区 内存溢出,方法区存放的是 类,静态常量,变量信息 * 所以 我们就用CGLIB 疯狂的创建类 就可以达到OOM了 */
public class MethodAreaOOM extends ClassLoader {

    public static void main(String[] args) {
        System.out.print("最大内存: ");
        System.out.println(Runtime.getRuntime().maxMemory() / 1024 / 1024 + "MB");
        System.out.print("可用内存: ");
        System.out.println(Runtime.getRuntime().freeMemory() / 1024 / 1024 + "MB");
        System.out.print("已使用内存: ");
        System.out.println(Runtime.getRuntime().totalMemory() / 1024 / 1024 + "MB");

        int count = 0;
        try {
            for (int i = 0; i < 10000; i++) {
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(MethodAreaOOM.class);
                enhancer.setUseCache(false);
                enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> methodProxy.invokeSuper(o, args));
                //cglib 创建类文件
                enhancer.create();
                count = i;

            }
        } catch (Throwable e) {
            System.out.println("****count=" + count);
            e.printStackTrace();
        }
    }
}

4.3 运行OOM日志

可以看到 日志java.lang.OutOfMemoryError: Metaspace 就是员工间方法区的OOM 溢出

"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m "-javaagent:E:\Program Files\JetBrains\IntelliJ IDEA 2019.3.4\lib\idea_rt.jar=59628:E:\Program Files\JetBrains\IntelliJ IDEA 2019.3.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\myworkspace\distribute\jvmtest\target\classes;E:\MavenRepository\org\springframework\boot\spring-boot-starter-web\2.5.4\spring-boot-starter-web-2.5.4.jar;E:\MavenRepository\org\springframework\boot\spring-boot-starter\2.5.4\spring-boot-starter-2.5.4.jar;E:\MavenRepository\org\springframework\boot\spring-boot\2.5.4\spring-boot-2.5.4.jar;E:\MavenRepository\org\springframework\boot\spring-boot-autoconfigure\2.5.4\spring-boot-autoconfigure-2.5.4.jar;E:\MavenRepository\org\springframework\boot\spring-boot-starter-logging\2.5.4\spring-boot-starter-logging-2.5.4.jar;E:\MavenRepository\ch\qos\logback\logback-classic\1.2.5\logback-classic-1.2.5.jar;E:\MavenRepository\ch\qos\logback\logback-core\1.2.5\logback-core-1.2.5.jar;E:\MavenRepository\org\apache\logging\log4j\log4j-to-slf4j\2.14.1\log4j-to-slf4j-2.14.1.jar;E:\MavenRepository\org\apache\logging\log4j\log4j-api\2.14.1\log4j-api-2.14.1.jar;E:\MavenRepository\org\slf4j\jul-to-slf4j\1.7.32\jul-to-slf4j-1.7.32.jar;E:\MavenRepository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;E:\MavenRepository\org\yaml\snakeyaml\1.28\snakeyaml-1.28.jar;E:\MavenRepository\org\springframework\boot\spring-boot-starter-json\2.5.4\spring-boot-starter-json-2.5.4.jar;E:\MavenRepository\com\fasterxml\jackson\core\jackson-databind\2.12.4\jackson-databind-2.12.4.jar;E:\MavenRepository\com\fasterxml\jackson\core\jackson-annotations\2.12.4\jackson-annotations-2.12.4.jar;E:\MavenRepository\com\fasterxml\jackson\core\jackson-core\2.12.4\jackson-core-2.12.4.jar;E:\MavenRepository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.12.4\jackson-datatype-jdk8-2.12.4.jar;E:\MavenRepository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.12.4\jackson-datatype-jsr310-2.12.4.jar;E:\MavenRepository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.12.4\jackson-module-parameter-names-2.12.4.jar;E:\MavenRepository\org\springframework\boot\spring-boot-starter-tomcat\2.5.4\spring-boot-starter-tomcat-2.5.4.jar;E:\MavenRepository\org\apache\tomcat\embed\tomcat-embed-core\9.0.52\tomcat-embed-core-9.0.52.jar;E:\MavenRepository\org\apache\tomcat\embed\tomcat-embed-el\9.0.52\tomcat-embed-el-9.0.52.jar;E:\MavenRepository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.52\tomcat-embed-websocket-9.0.52.jar;E:\MavenRepository\org\springframework\spring-web\5.3.9\spring-web-5.3.9.jar;E:\MavenRepository\org\springframework\spring-beans\5.3.9\spring-beans-5.3.9.jar;E:\MavenRepository\org\springframework\spring-webmvc\5.3.9\spring-webmvc-5.3.9.jar;E:\MavenRepository\org\springframework\spring-aop\5.3.9\spring-aop-5.3.9.jar;E:\MavenRepository\org\springframework\spring-context\5.3.9\spring-context-5.3.9.jar;E:\MavenRepository\org\springframework\spring-expression\5.3.9\spring-expression-5.3.9.jar;E:\MavenRepository\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;E:\MavenRepository\org\springframework\spring-core\5.3.9\spring-core-5.3.9.jar;E:\MavenRepository\org\springframework\spring-jcl\5.3.9\spring-jcl-5.3.9.jar" com.jzj.jvmtest.oomtest.MethodAreaOOM 最大内存: 19MB 可用内存: 16MB 已使用内存: 19MB [GC (Allocation Failure) [PSYoungGen: 8192K->1000K(9216K)] 8192K->1709K(19456K), 0.0013246 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 9192K->1000K(9216K)] 9901K->2255K(19456K), 0.0011901 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] ... [Full GC (Metadata GC Threshold) [PSYoungGen: 320K->0K(8192K)] [ParOldGen: 5896K->3747K(10240K)] 6216K->3747K(18432K), [Metaspace: 10049K->10049K(1058816K)], 0.0380225 secs] [Times: user=0.28 sys=0.00, real=0.04 secs] [GC (Last ditch collection) [PSYoungGen: 0K->0K(8192K)] 3747K->3747K(18432K), 0.0003694 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Last ditch collection) [PSYoungGen: 0K->0K(8192K)] [ParOldGen: 3747K->2210K(10240K)] 3747K->2210K(18432K), [Metaspace: 10049K->10042K(1058816K)], 0.0231525 secs] [Times: user=0.16 sys=0.00, real=0.02 secs] java.lang.OutOfMemoryError: Metaspace Dumping heap to java_pid5592.hprof ... Heap dump file created [4525675 bytes in 0.014 secs] [GC (Metadata GC Threshold) [PSYoungGen: 122K->96K(8192K)] 2333K->2306K(18432K), 0.0005350 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Metadata GC Threshold) [PSYoungGen: 96K->0K(8192K)] [ParOldGen: 2210K->2210K(10240K)] 2306K->2210K(18432K), [Metaspace: 10043K->10043K(1058816K)], 0.0095820 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] [GC (Last ditch collection) [PSYoungGen: 0K->0K(8192K)] 2210K->2210K(18432K), 0.0003715 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Last ditch collection) [PSYoungGen: 0K->0K(8192K)] [ParOldGen: 2210K->2210K(10240K)] 2210K->2210K(18432K), [Metaspace: 10043K->10043K(1058816K)], 0.0043271 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] ****count=208 Heap PSYoungGen total 8192K, used 246K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) eden space 6144K, 4% used [0x00000000ff600000,0x00000000ff63d878,0x00000000ffc00000) from space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000) to space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000) ParOldGen total 10240K, used 2210K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) object space 10240K, 21% used [0x00000000fec00000,0x00000000fee28ad8,0x00000000ff600000) Metaspace used 10074K, capacity 10174K, committed 10240K, reserved 1058816K class space used 787K, capacity 851K, committed 896K, reserved 1048576K java.lang.OutOfMemoryError: Metaspace at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:557) at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:363) at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:585) at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:131) at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:319) at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:572) at org.springframework.cglib.proxy.Enhancer.create(Enhancer.java:387) at com.jzj.jvmtest.oomtest.MethodAreaOOM.main(MethodAreaOOM.java:28) Process finished with exit code 0 

5.本机直接内存溢出

5.1 设置JVM参数
-verbose:gc -Xms20M -Xmx20M  -Xmn10M -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:SurvivorRatio=8 -XX:MaxDirectMemorySize=5M

这里新加了参数-XX:MaxDirectMemorySize=5M 来控制本地直接内存的大小

  1. -XX:MaxDirectMemorySize=10M:设置本地直接内存大小
5.2 测试代码
package com.jzj.jvmtest.oomtest;

import sun.misc.VM;

import java.nio.ByteBuffer;

/** * 本地直接内存溢出 * 参数-XX:MaxDirectMemorySize=10M 来控制本地直接内存的大小 */
public class LocalDirectOOM extends ClassLoader {
    private static final int ONE_MB = 1024 * 1024;

    public static void main(String[] args) throws Exception {
        System.out.print("最大内存: ");
        System.out.println(Runtime.getRuntime().maxMemory() / 1024 / 1024 + "MB");
        System.out.print("可用内存: ");
        System.out.println(Runtime.getRuntime().freeMemory() / 1024 / 1024 + "MB");
        System.out.print("已使用内存: ");
        System.out.println(Runtime.getRuntime().totalMemory() / 1024 / 1024 + "MB");

        //加了参数 -XX:MaxDirectMemorySize=5M 后 ,打印出来就是 5M , 默认的就是 最大内存大小19M
        System.out.println("maxDirectMemorym设置大小" + VM.maxDirectMemory() / 1024 / 1024 + "MB");

        //分配最大本地内存是1000000MB
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1000000 * 1024 * 1024);


    }
}
5.3 运行OOM日志

可以看到 日志java.lang.OutOfMemoryError: Direct buffer memory . 就是本地直接内存 OOM溢出

最大内存: 19MB
可用内存: 16MB
已使用内存: 19MB
maxDirectMemorym设置大小5MB
[GC (System.gc()) [PSYoungGen: 2683K->992K(9216K)] 2683K->1108K(19456K), 0.0007256 secs] [Times: user=0.16 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 992K->0K(9216K)] [ParOldGen: 116K->822K(10240K)] 1108K->822K(19456K), [Metaspace: 3440K->3440K(1056768K)], 0.0039306 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
	at java.nio.Bits.reserveMemory(Bits.java:694)
	at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
	at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
	at com.jzj.jvmtest.oomtest.LocalDirectOOM.main(LocalDirectOOM.java:28)
Heap
 PSYoungGen      total 9216K, used 573K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 7% used [0x00000000ff600000,0x00000000ff68f778,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 10240K, used 822K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 8% used [0x00000000fec00000,0x00000000feccd9f8,0x00000000ff600000)
 Metaspace       used 3520K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 386K, capacity 388K, committed 512K, reserved 1048576K

Process finished with exit code 1

综上所述 ,我们来总结一下

  1. 堆溢出, 堆中存放对象,线程共享 ———— 疯狂创建对象就可以导致堆溢出 Java heap space OOM
  2. java虚拟机栈溢出,本地方法栈溢出 ,栈存放线程私有,refer指针, 局部变量表存放 对象8大数据类型,方法出口信息, 对象引用refer指针 ———— 可以递归创建对象,如果栈深度大于虚拟机允许最大深度,或者再次创建对象是不允许扩展栈 就会 StackOverFlow 或者 OOM
  3. 方法区溢出,方法区存放 类、静态变量,常量,线程共享,称为非堆, jdk1.7以前永久区,jdk1.8以后元空间 ———— 疯狂创建常量Str信息或者cglib 创建类 Class信息,就会导致 Perm Space OOM 或者 Meta Space OOM
  4. 本地直接内存, 一般都是指定本地空间,没指定就是最大内存 ———— ByteBuffer.allocateDirect 超出空间无法分配时 抛出 : Direct buffer memory OOM

今天的文章JVM调优-OutOfMemoryError 分析分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/21904.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注