java基础知识JVM——OutOfMemoryError演示之6种OOM

OutOfMemoryError演示

第一种,StackOverflowError

栈满会抛出该错误。无限递归就会导致StackOverflowError,是java.lang.Throwablejava.lang.Errorjava.lang.VirtualMachineError下的错误。

代码

package jvm;

public class StackOverflowErrorDemo {
    
    
    public static void main(String[] args) {
    
    
        stackOverflowError();
    }

    private static void stackOverflowError() {
    
    
        stackOverflowError();
    }

}

输出

Exception in thread "main" java.lang.StackOverflowError
	at jvm.StackOverflowErrorDemo.stackOverflowError(StackOverflowErrorDemo.java:9)
	at jvm.StackOverflowErrorDemo.stackOverflowError(StackOverflowErrorDemo.java:9)

第二种,Java heap space

栈满会抛出该错误。
IDEA菜单栏---Run---Edit Configurations ---VM options填写以下参数

-Xms10m -Xmx10m

最后,右键run运行。

代码

package jvm;

import java.util.Random;

//-Xms10m -Xmx10m -XX:MaxDirectMemorySize=5m -XX:+PrintGCDetails
public class JavaHeapSpaceDemo {
    
    
    public static void main(String[] args) {
    
    
        String str = "adf";
        while (true) {
    
    
            str += str + new Random().nextInt(1111111) + new Random().nextInt(222222);
            str.intern();
        }
    }
}

输出

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:3332)
	at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
	at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674)
	at java.lang.StringBuilder.append(StringBuilder.java:208)
	at jvm.JavaHeapSpaceDemo.main(JavaHeapSpaceDemo.java:10)

GC overhead limit exceeded

GC回收时间过长时会地出 outofMemroyError。过长的定义是,则过98%的时间用来做GC并且回收了不到2%的堆内
连续多次GC都只回收了不到2%的极端情况下才会抛出。假如不抛出 GC overhead limit错误会发生什么情况呢?
那就是GC清理的这么点内存很块会再次填满,道使GC再次执行,这样就形成恶性循坏
CPU使用率一直是100%,GC却没有任何成果

代码

package jvm;

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

//-Xms10m -Xmx10m -XX:MaxDirectMemorySize=5m -XX:+PrintGCDetails
public class GCOverheadDemo {
    
    
    public static void main(String[] args) {
    
    
        int i = 0;
        List<String> list = new ArrayList<>();
        try {
    
    
            while (true) {
    
    
                list.add(String.valueOf(++i).intern());
            }
        } catch (Exception e) {
    
    
            System.out.println("************i" + i);
            e.printStackTrace();
            throw e;
        }
    }
}

输出
GCOverheadLimit
详细情况

[GC (Allocation Failure) [PSYoungGen: 2035K->496K(2560K)] 2035K->1020K(9728K), 0.0016816 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 2544K->505K(2560K)] 3068K->2701K(9728K), 0.0024345 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 2553K->512K(2560K)] 4749K->4593K(9728K), 0.0030155 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 2560K->496K(2560K)] 6641K->6650K(9728K), 0.0037446 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 496K->0K(2560K)] [ParOldGen: 6154K->6218K(7168K)] 6650K->6218K(9728K), [Metaspace: 3214K->3214K(1056768K)], 0.0721823 secs] [Times: user=0.27 sys=0.00, real=0.07 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2048K->824K(2560K)] [ParOldGen: 6218K->7020K(7168K)] 8266K->7845K(9728K), [Metaspace: 3214K->3214K(1056768K)], 0.0489867 secs] [Times: user=0.23 sys=0.00, real=0.05 secs] 
...
...
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7061K->7061K(7168K)] 9109K->9109K(9728K), [Metaspace: 3218K->3218K(1056768K)], 0.0330914 secs] [Times: user=0.13 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7063K->7063K(7168K)] 9111K->9111K(9728K), [Metaspace: 3218K->3218K(1056768K)], 0.0327293 secs] [Times: user=0.08 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7065K->7065K(7168K)] 9113K->9113K(9728K), [Metaspace: 3218K->3218K(1056768K)], 0.0278034 secs] [Times: user=0.20 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7067K->7067K(7168K)] 9115K->9115K(9728K), [Metaspace: 3218K->3218K(1056768K)], 0.0292778 secs] [Times: user=0.22 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->0K(2560K)] [ParOldGen: 7092K->614K(7168K)] 9140K->614K(9728K), [Metaspace: 3249K->3249K(1056768K)], 0.0072447 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 2560K, used 166K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
  eden space 2048K, 8% used [0x00000000ffd00000,0x00000000ffd29ad0,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
 ParOldGen       total 7168K, used 614K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
  object space 7168K, 8% used [0x00000000ff600000,0x00000000ff699b60,0x00000000ffd00000)
 Metaspace       used 3325K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 359K, capacity 388K, 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 jvm.GCOverheadDemo.main(GCOverheadDemo.java:13)

Direct buffer memory

写NIO程序经常使用 ByteBuffer来读或者写入数据,这是一种基于通道( Channel)与缓冲区( Buffer)的 I/O方式,
它可以使用 Native函数库直接分配堆外内存,然后通过一个存在Java里面 DirectByteBuffer0象作为这块内存的引用进行操作
这样能在一些场景中显著提高性能,因为避免了在Jva和 Native堆中来回复制数据。
ByteBuffer.allocate( capability)第一种方式是分配W内存,属GC管辖范国,由于需要贝所以速度相较慢
ByteBuffer.allocateDirect( capability)第一种方式是分配OS本地内存,不属GC管接范围,由于下需要内存拷贝所纵速度相对较快
但如果不断分配本地内存,推内存很少使用,那么W就不需要行GC, DirectByte Buffer对象们就不会被回收,
时候内存充足,但本地内存可能已经使用光了,再尝试分配本地内存就会出现0ut0 mEmory Error,那程序就直接 崩溃了。

代码

package jvm;

import java.nio.ByteBuffer;

//-Xms10m -Xmx10m -XX:MaxDirectMemorySize=5m -XX:+PrintGCDetails
public class DirectBufferMemoryDemo {
    
    
    public static void main(String[] args) {
    
    
        System.out.println("配置的maxDirectMemory: " 
        + (sun.misc.VM.maxDirectMemory() / (double) 1024 / 1024) + "MB");
        try {
    
    
            Thread.sleep(300);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);
    }
}

OOM—unable to create new native thread

在高并发应用场景时,如果创建超过了系统默认的最大线程数,就会抛出该异常。Linux单个进程默认不能超过1024个线程。解决方法要么降低程序线程数,要么修改系统最大线程数vim /etc/security/limits.d/90-nproc.conf

package jvm;

public class UnableCreateNewThreadDemo {
    
    
    public static void main(String[] args) {
    
    
        for (int i = 0; ; i++) {
    
    
            System.out.println("***********" + i);
            new Thread(() -> {
    
    
                try {
    
    
                    Thread.sleep(Integer.MAX_VALUE);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }, "" + i).start();
        }
    }
}

输出

***********0
***********1
...
...
***********47842
***********47843
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
	at java.lang.Thread.start0(Native Method)
	at java.lang.Thread.start(Thread.java:717)
	at jvm.UnableCreateNewThreadDemo.main(UnableCreateNewThreadDemo.java:13)

OOM—Metaspace

元空间满了就会抛出这个异常。

代码

package jvm;


import java.lang.reflect.Method;

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

/**
 * java 8及之后版本使用Metaspace来代替永久代
 * -XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=8m
 *
 * 永久代(java8使用Metaspace)存放的信息:
 * 虚拟机加载的类信息
 * 常量池
 * 静态变量
 * 即时编译后的代
 *
 * 模拟Metaspace空间溢出,我们不断生成类往空间灌,类占据的空间总是会超时Metaspace指定的空间大小的
 */
public class MetaspaceOOMTest {
    
    

    static class OOMTest {
    
    
    }

    public static void main(String[] args) {
    
    
        int i = 0;
        try {
    
    
            while (true) {
    
    
                i++;
                // 使用CGLIB生成代理:
                // 1.创建核心类:
                Enhancer enhancer = new Enhancer();
                // 2.为其设置父类:
                enhancer.setSuperclass(OOMTest.class);
                // 3.设置回调:
                enhancer.setCallback(new MethodInterceptor() {
    
    
                    @Override
                    public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy)
                            throws Throwable {
    
    
                        return methodProxy.invokeSuper(object, args);
                    }
                });
                enhancer.create();
            }
        } catch (Throwable e) {
    
    
            System.out.println("**********多少次后发生了异常:  " + i);
            e.printStackTrace();
        }
    }
}

输出

"C:\Program Files\Java\jdk1.8.0_191\bin\java.exe" ...
Error occurred during initialization of VM
MaxMetaspaceSize is too small.

报的错误有点不一样,正常来说应该是报这个的,可能是版本不一样吧。
在这里插入图片描述

参考资料

OOM之Metaspace
009. 深入JVM学习—元空间

猜你喜欢

转载自blog.csdn.net/e891377/article/details/108771699
今日推荐