基础 | JVM - [内存溢出]

§1 StackOverFlowError

  • StackOverFlowError 是栈溢出错误,Error 可以将 JVM 玩崩
  • 因为 JVM 的虚拟机栈通常是 1M 的,所以相对容易触发
  • 造成 StackOverFlowError 的最常见场景是 超长递归

§2 OutOfMemoryError

  • 内存溢出错误,Error 可以将 JVM 玩崩
  • 根据溢出部位和原因的不同,OutOfMemoryError 分如下几种常见分支
§2.1 java heap space
  • java heap space 是 JVM 中 不够用了

弄一个出来
设置 JVM 只有 3M 的堆 -Xms3m -Xmx3m
可以玩命创建对象

for(String s = "";;) s+= UUID.randomUUID();

也可以一口气来个大的

int[] a = new int[10 * 1024*1024];

在这里插入图片描述

§2.2 GC overhead limit exceeded
  • GC 开销超出限制
  • 常出现 GC 时间占一个 java 进程 98% 的总时间以上,并且至少已经这样进行了 5 次 GC 后

官方说明

The detail message “GC overhead limit exceeded” indicates that the garbage collector is running all the time and Java program is making very slow progress. After a garbage collection, if the Java process is spending more than approximately 98% of its time doing garbage collection and if it is recovering less than 2% of the heap and has been doing so far the last 5 (compile time constant) consecutive garbage collections, then a java.lang.OutOfMemoryError is thrown. This exception is typically thrown because the amount of live data barely fits into the Java heap having little free space for new allocations.
Action: Increase the heap size. The java.lang.OutOfMemoryError exception for GC Overhead limit exceeded can be turned off with the command line flag -XX:-UseGCOverheadLimit.

“GC overhead limit exceeded” 说明 java 进程处理的很慢,因为总是在 GC
一次 GC 之后,如果 java 进程花费了 98% 以上的时间用于 GC 并且它只回收了不到 2% 堆空间,同时这样的 GC 至少已经发生了 5 次以上,就会抛出 java.lang.OutOfMemoryError
此异常由下面原因导致:堆空间勉强存放了存活的数据,只有很小的空间留给新的指派(对象)使用
行动建议:

  • 增加堆的大小
  • 通过 JVM 参数 -XX:-UseGCOverheadLimit 关闭 GC 开销限制

弄一个出来
设置 JVM 只有 3M 的堆 -Xms3m -Xmx3m -XX:+PrintGCDetails

public class GcOverheadBoomDemo {
    public static void main(String[] args) {
        List list = new ArrayList();
        for(;;){
            list.add(UUID.randomUUID());
        }
    }
}

在这里插入图片描述

§2.3 Direct buffer memory
  • JVM 的直接缓冲内存耗尽
    Direct buffer memory 指的是 直接内存 的一部分,作为 NIO 的 Buffer 区使用,直接内存 的相关内容 点此
  • 常出现于通过 NIO 的 ByteBuffer.allocteDirect(capability) 频繁使用直接缓冲内存,且没有保证 DirectByteBuffer 指向的直接内存及时 GC 时
    • allocteDirect(capability) 会在直接内存申请一块 Direct buffer,并通过 jvm 堆 中的 DirectByteBuffer 对象引用和操作
    • 这么做免于数据从 native 堆 复制到 jvm 堆 的开销,也免于因此造成的 GC 的开销
    • DirectByteBuffer 对象不可达时,GC 过程中会回收对应的直接缓冲内存
    • 若 DirectByteBuffer 对象进入老生代,则只有 Full GC 才有可能回收对应的直接缓冲内存
    • 使用 IOUtil 操作非 DirectBuffer IO 操作时,会使用一个临时的 DirectByteBuffer 来实际执行,并在使用后将它缓存到 ThreadLocal,因此可能造成 GC 不及时
  • 可以通过 JVM 参数 -XX:MaxDirectMemorySize 配置较大的直接内存

弄一个出来
设置 JVM 只有 5M 的直接内存 -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m

ByteBuffer buffer = ByteBuffer.allocateDirect(30 * 1024*1024);

在这里插入图片描述

§2.5 Metaspace
  • 元空间耗尽
  • 常出现于大量通过 CGlib 创建代理类(关键是类,不是对象)导致大量占用元空间的场景
  • 通过声明一个常量,常量引用大对象是不会触发此报错的,详细可见 基础 | JVM - [内存模型 & JMM]

弄一个出来

static class SDf{
}

public static void generate(String[] args){
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(SDf.class);
    enhancer.setUseCache(false);
    enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> methodProxy.invoke(o,args));
    enhancer.create();
}

public static void main(String[] args) {
    for(int i=0;;i++){
        generate(args);
        System.out.println(i);
    }
}

在这里插入图片描述

§2.5 unable to create new native thread
  • 不能创建新的本地线程
    JVM 在开新线程时是通过 private native void start0(); 进行的,可能这个本地方法就是提示信息中 native 的由来
  • 常出现与应用创建了太多线程,以致于超出系统限制
    Linux 默认单个进程可以创建的线程数为 1024
  • 通常情况下,需要降低应用线程数
  • 一定需要这么多线程时需要变更系统设置

弄一个出来
不要随便尝试,尤其不要用 windows 尝试
不要随便尝试,尤其不要用 windows 尝试
不要随便尝试,尤其不要用 windows 尝试
我就想截个效果图,死机了 5 次,看到是看见了,但是已经无法截图了,每次都是 cpu 100,内存 100

for(;;){
   new Thread(()->{
       try { TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); }
   }).start();
}

在这里插入图片描述

修改 Linux 线程数设置
查看当前系统允许创建的线程数
ulimit -u

修改对应配置
vim /etc/security/limits.d/90-nproc.conf

添加
用户名 soft nproc 新数值

默认情况下,root 用户无限制,其他用户 1024

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值