JVM调优OutOfMemoryError异常分析

目录
  • 1.Java 堆溢出
    • 1.1 设置JVM参数
    • 1.2 测试代码
    • 1.3 运行OOM日志
  • 2.Java栈、本地方法栈溢出
    • 2.1 设置JVM参数
    • 2.2 测试代码
    • 2.3 运行OOM日志
    • 2.4 Java虚拟机OOM异常
  • 3.Java 运行常量池溢出
    • 3.1 设置JVM参数-注意区分jdk版本
    • 3.2 测试代码
    • 3.3 运行OOM日志
  • 4.Java 方法区溢出-jdk8
    • 4.1 设置JVM参数
    • 4.2 测试代码
    • 4.3 运行OOM日志
  • 5.本机直接内存溢出
    • 5.1 设置JVM参数
    • 5.2 测试代码
    • 5.3 运行OOM日志

1.Java 堆溢出

1.1 设置JVM参数

-verbose:gc -Xms20M -Xmx20M -Xmn10M  -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:SurvivorRatio=8

  • -Xmx20m:设置JVM最大可用内存为20M。
  • -Xms20m:设置JVM促使内存为20m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内
  • -Xmn10m:设置年轻代大小为10m
  • -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 设置每个线程的堆栈大小

  • 虚拟机扩展栈时,如果栈深度大于虚拟机允许最大深度,就会出现StackOverflowError异常
  • 如果在扩展栈时,需要的内存空间不够,那么就会出现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呢?

分析:

  • 假设操作系统有2G内存
  • 虚拟机可以通过参数来控制虚拟机堆 和 方法区允许内存的最大值
  • 堆最大值 -Xmx 控制,方法区永久区 -MaxPermSize最大方法区容量控制方法区大小
  • 2GB 减去 堆大小, 减去 方法区大小, 剩下的内存就由 java 虚拟机栈与本地方法栈共享瓜分
  • 每个线程分配的空间越大, 那么系统可以操作的线程数就越少,那么新建线程就越容易超标,造成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 来控制方法区大小

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

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

  • 在 JDK 1.6 及之前的版本中,由于常量池分配在永久代内,我们可以通过 -XX:PermSize 和 -XX:MaxPermSize 限制方法区大小,从而间接限制其中常量池的容量
  • 但是 在 JDK1.8运行这段程序不会得到相同的结果,而是出现以下的提示信息,这是因为这两个参数已经不在JDK1.7中使用了。
  • 提示错误 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

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

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这里新加了参数 来控制方法区大小

  • -XX:MetaspaceSize=5m,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,
  • -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这里新加了参数 来控制方法区大小

  • -XX:MetaspaceSize=5m,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,
  • -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 来控制方法区大小

  • -XX:MetaspaceSize=10m,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,
  • -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 来控制本地直接内存的大小

  • -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 1048576KProcess finished with exit code 1

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

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

以上就是JVM调优OutOfMemoryError异常分析的详细内容,更多关于JVM调优OutOfMemoryError异常的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java中内存异常StackOverflowError与OutOfMemoryError详解

     Java中内存异常StackOverflowError与OutOfMemoryError详解 使用Java开发,经常回遇到内存异常的情况,而StackOverflowError和OutOfMemoryError便是最常遇见的错误. 首先,看看这两种错误的解释: 如果当前线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常. 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常. 这里把异常分为两种情况,但是存在一些相互重

  • 实例解决Java异常之OutOfMemoryError的问题

    在Java虚拟机规范描述中,除了程序计数器外,虚拟机内存的其他几个运行区域都有发生 OOM 异常的可能.在这里,用代码验证各个运行时区域存储的内容并讨论该如何进行处理. Java堆溢出 Java 堆用于存储对象实例,只要不断创建对象,并且保证 GC Roots 到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么对象数量达到最大堆的容量限制之后就会产生内存溢出异常. 异常再现 代码采用如下虚拟机参数: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryErr

  • java.lang.OutOfMemoryError: Metaspace异常解决的方法

    介绍 Metaspace java8 及以后的版本使用Metaspace来代替永久代,Metaspace是方法区在HotSpot中的实现,它与持久代最大区别在于,Metaspace并不在虚拟机内存中而是使用本地内存也就是在JDK8中,classe metadata(the virtual machines internal presentation of Java class),被存储在叫做Metaspace的native memory. 永久代(java 8 后被元空间Metaspace取代了

  • Java实战之OutOfMemoryError异常问题及解决方法

    目录 一.简言 二.代码实战 1.Java堆溢出 2.虚拟机栈和本地方法栈溢出 3.运行时常量池溢出 4.方法区溢出 5.本机直接内存溢出 三.JVM常用的启动参数 四.面试题 五.总结 在Java虚拟机规范的描述中,除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError (下文称OOM)异常的可能.本篇主要结合着[深入理解Java虚拟机]一书当中整理了本篇博客,感兴趣的跟着小编一块来学习呀! 本篇文章和上一篇写到的 Java内存区域划分 息息相关,如果您对Ja

  • 完美解决java.lang.OutOfMemoryError处理错误的问题

    原因: 常见的有以下几种: 1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据: 2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收: 3.代码中存在死循环或循环产生过多重复的对象实体: 4.使用的第三方软件中的BUG: 5.启动参数内存值设定的过小: 常见错误提示: 1.tomcat:java.lang.OutOfMemoryError: PermGen space 2.tomcat:java.lang.OutOfMemoryError: Java heap space 3

  • 解决Eclipse Tomcat OutOfMemoryError:PermGen space的问题

    PermGen space的全称是Permanent Generation space,是指内存的永久保存区域OutOfMemoryError: PermGen space.从文字上看就是内存溢出,解决方法是加大内存.为什么会内存溢出,这是由于这块内存主要是被JVM存放Class和Meta信息的,Class在被Load的时候被放入PermGen space区域,它和存放Instance的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进

  • JVM调优OutOfMemoryError异常分析

    目录 1.Java 堆溢出 1.1 设置JVM参数 1.2 测试代码 1.3 运行OOM日志 2.Java栈.本地方法栈溢出 2.1 设置JVM参数 2.2 测试代码 2.3 运行OOM日志 2.4 Java虚拟机OOM异常 3.Java 运行常量池溢出 3.1 设置JVM参数-注意区分jdk版本 3.2 测试代码 3.3 运行OOM日志 4.Java 方法区溢出-jdk8 4.1 设置JVM参数 4.2 测试代码 4.3 运行OOM日志 5.本机直接内存溢出 5.1 设置JVM参数 5.2 测

  • 基于JVM 调优的技巧总结分析

    这篇是技巧性的文章,如果要找关于GC或者调整内纯的文章,看我其他几篇文章.因为是JVM 调优总结,所以废话少说.从各方面一共收集到以下几个方法:1.升级 JVM 版本.如果能使用64-bit,使用64-bit JVM.    基本上没什么好解释的,很简单将JVM升级到最新的版本.如果你还是使用JDK1.4甚至是更早的JVM,那你首先要做的就是升级.因为JVM从1.4- >1.5->1.6可不是仅仅的版本号升级,或者仅仅往里面加了一堆新的语言特性,这么简单.而是真正在JVM做了重大的改进,每次版

  • jvm垃圾回收GC调优基础原理分析

    目录 核心概念(Core Concepts) Latency(延迟) Throughput(吞吐量) Capacity(系统容量) 相关示例 Tuning for Latency(调优延迟指标) Tuning for Throughput(吞吐量调优) Tuning for Capacity(调优系统容量) 说明: Capacity: 性能,能力,系统容量; 文中翻译为”系统容量“; 意为硬件配置. GC调优(Tuning Garbage Collection)和其他性能调优是同样的原理.初学者

  • jvm调优的几种场景(小结)

    目录 一.cpu占用过高 (1)用top命令查看cpu占用情况 (2)用top -Hp命令查看线程的情况 (3)把线程号转换为16进制 (4)用jstack工具查看线程栈情况 二.死锁 三.内存泄漏 四.总结 假定你已经了解了运行时的数据区域和常用的垃圾回收算法,也了解了Hotspot支持的垃圾回收器. 一.cpu占用过高 cpu占用过高要分情况讨论,是不是业务上在搞活动,突然有大批的流量进来,而且活动结束后cpu占用率就下降了,如果是这种情况其实可以不用太关心,因为请求越多,需要处理的线程数越

  • Jvm调优和SpringBoot项目优化的详细教程

    一.Jvm调优. 参考文章 1.先看一下未设置JVM参数的情况,默认情况下,没有设置任何Jvm参数. idea中安装VisualVM监控jvm的图文教程 2.设置Jvm参数. 配置参数: 关于这些设置的JVM参数是什么意思,参考Jvm调优. -XX:MetaspaceSize=128m (元空间默认大小) -XX:MaxMetaspaceSize=128m (元空间最大大小) -Xms1024m (堆最大大小) -Xmx1024m (堆默认大小) -Xmn256m (新生代大小) -Xss256

  • java虚拟机之JVM调优详解

    JVM常用命令行参数 1. 查看参数列表 虚拟机参数分为基本和扩展两类,在命令行中输入 JAVA_HOME\bin\java就可得到基本参数列表. 在命令行输入 JAVA_HOME\bin\java –X就可得到扩展参数列表. 2. 基本参数说明: -client,-server: 两种Java虚拟机启动方式,client模式启动比较快,但是性能和内存管理相对较差,server模式启动比较慢,但是运行性能比较高,windos上采用的是client模式,Linux采用server模式 -class

  • Java JVM调优五大技能详解

    目录 1.什么时候需要JVM调优 2.JVM调优一般调什么 3.JVM调优基本步骤 3.1添加GC日志相关的参数 3.2添加内存溢出与Full gc前快照输出参数 3.3通过日志确定问题 3.3.1堆内存不足 3.3.2频繁Full gc 4.监控工具 4.1使用jstat 统计gc相关信息 4.2使用jmap命令查某时刻的JVM堆信息 5.常用的调优工具有哪些? 总结 1.什么时候需要JVM调优 应用的响应慢.CPU占用高 应用吞吐量小,占用内存空间过大 这些表象一般伴随着频繁的垃圾回收,或者

  • JProfiler11使用教程之JVM调优问题小结

    安装JProfiler jprofiler_windows-x64_11_0_2 链接: https://pan.baidu.com/s/1EWxW5VT100D1v_HVvKYGqQ?pwd=qif5 提取码: qif5 JProfiler11破解 然后打开破解机器 KeyGen.exe 链接: https://pan.baidu.com/s/13MX6iLFtcmerdGovYjOh4g?pwd=cx7e 提取码: cx7e 配置本地监控 我们启动一个本地项目weblogic,Jboss,t

  • 优化Java虚拟机总结(jvm调优)

    堆设置 -Xmx3550m:设置JVM最大堆内存为3550M. -Xms3550m:设置JVM初始堆内存为3550M.此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存. -Xss128k:设置每个线程的栈大小.JDK5.0以后每个线程栈大小为1M,之前每个线程栈大小为256K.应当根据应用的线程所需内存大小进行调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右. -Xmn2g:设置堆

  • Java JVM原理与调优_动力节点Java学院整理

    JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的.Java虚拟机包括一套字节码指令集.一组寄存器.一个栈.一个垃圾回收堆和一个存储方法域. JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行.是运行Java应用最底层部分. JDK(Java Development kit) 整个Java的核心,包括了Java运行环境(Java Runtime E

随机推荐