一个OutOfMemoryError的实例

本文介绍了一种引发OutOfMemoryError的方法,通过无限创建并启动线程,可能导致计算机死机。此方法涉及线程管理和内存使用,谨慎使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        OutOfMemoryError,即内存溢出异常,代码中常见的错误,一下介绍一个重量级的武器,坑害他人必备。

友情提示:该行为有可能导致电脑死机。

@Test
public void stackThread(){
    while(true){
        new Thread(){
            @Override
            public void run() {
                while(true){

                }
            }
        }.start();
    }
}

 

<think>我们正在讨论Java中的OutOfMemoryError分类。根据JVM规范,OutOfMemoryError是Error的子类,表示内存不足。根据内存区域的不同,OutOfMemoryError有不同的子类型或不同的原因。主要可以分为以下几种: 1. **Java堆空间溢出(OutOfMemoryError: Java heap space)** 这是最常见的OOM,表示Java堆内存不足,无法分配对象。当创建的对象超过堆的最大容量(通过`-Xmx`设置)时抛出。例如,不断创建大对象: ```java List<byte[]> list = new ArrayList<>(); while (true) { list.add(new byte[1024 * 1024]); // 每次分配1MB } ``` 2. **元空间溢出(OutOfMemoryError: Metaspace)** 在Java 8及以后,永久代(PermGen)被元空间(Metaspace)取代。元空间用于存储类的元数据,当加载的类过多时(如动态生成大量类),如果元空间内存不足,就会抛出此错误。可以通过`-XX:MaxMetaspaceSize`设置元空间大小。 3. **GC开销超过限制(OutOfMemoryError: GC Overhead limit exceeded)** 当JVM花费大量时间进行垃圾回收(例如超过98%的时间)但只回收了很少的内存(例如不到2%的堆空间),就会抛出此错误。这通常意味着堆内存太小或者应用程序存在内存泄漏。 4. **无法创建新的本地线程(OutOfMemoryError: unable to create new native thread)** 当JVM向操作系统申请创建新的线程时,如果操作系统无法分配资源(例如进程的线程数达到上限,或者虚拟内存不足),就会抛出此错误。可以通过调整系统参数(如`ulimit -u`)或减少线程数来解决。 5. **请求的数组大小超过虚拟机限制(OutOfMemoryError: Requested array size exceeds VM limit)** 当尝试分配一个超过JVM允许的最大数组大小时抛出。数组大小的上限约为`Integer.MAX_VALUE - 8`(即2147483639),但实际最大值取决于JVM实现和可用内存。 6. **直接内存溢出(OutOfMemoryError: Direct buffer memory)** 直接内存(Direct Memory)不是JVM堆内存,而是通过`ByteBuffer.allocateDirect()`分配的内存。当直接内存不足时抛出。可以通过`-XX:MaxDirectMemorySize`设置直接内存大小。 7. **方法区溢出(在Java 8之前为OutOfMemoryError: PermGen space)** 在Java 8之前,永久代用于存储类的元数据、常量池等。如果加载的类过多,或者字符串常量池过大,可能导致永久代溢出。在Java 8及以后,此区域被元空间取代。 8. **代码缓存溢出(OutOfMemoryError: CodeCache is full)** JIT编译器将热点代码编译成本地代码后存储在CodeCache区域。如果CodeCache区域被填满,就会抛出此错误。可以通过`-XX:ReservedCodeCacheSize`调整大小。 9. **交换空间溢出(OutOfMemoryError: Out of swap space)** 当操作系统的交换空间(虚拟内存)不足时,JVM无法继续分配内存,会抛出此错误。这通常是由于系统级别内存不足引起的。 10. **本地方法溢出(OutOfMemoryError: reason stack_trace_with_native_method)** 当在本地方法(Native Method)执行过程中发生内存分配失败时抛出,通常是由于本地堆内存不足。 ### 示例代码:堆内存溢出 ```java // 设置JVM参数:-Xms10m -Xmx10m public class HeapOOM { public static void main(String[] args) { List<byte[]> list = new ArrayList<>(); while (true) { list.add(new byte[1024 * 1024]); // 每次分配1MB,直到堆溢出 } } } // 运行结果:java.lang.OutOfMemoryError: Java heap space ``` ### 总结表格 | 错误类型 | 触发原因 | 解决方案 | |------------------------------------------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------| | **Java heap space** | 堆内存不足 | 增大堆大小(-Xmx),优化内存使用 | | **Metaspace** | 类元数据超出元空间容量 | 增大元空间(-XX:MaxMetaspaceSize),检查类加载情况 | | **GC Overhead limit exceeded** | GC效率低下(长时间回收很少内存) | 检查内存泄漏,调整堆大小,优化GC策略 | | **unable to create new native thread** | 操作系统无法创建新线程 | 减少线程数,调整系统线程数限制 | | **Requested array size exceeds VM limit** | 数组大小超过JVM限制(接近Integer.MAX_VALUE) | 检查数组分配逻辑,使用分块处理 | | **Direct buffer memory** | 直接内存不足 | 调整-XX:MaxDirectMemorySize,确保及时清理直接缓冲区 | | **PermGen space**(Java 7及以前) | 永久代(类元数据、常量池)溢出 | 增大永久代(-XX:MaxPermSize),检查类加载器泄漏 | | **CodeCache is full** | JIT编译代码缓存区满 | 增大CodeCache(-XX:ReservedCodeCacheSize),减少编译的代码量 | | **Out of swap space** | 操作系统交换空间不足 | 增加系统虚拟内存,减少JVM内存占用 | | **reason stack_trace_with_native_method** | 本地方法执行中内存分配失败 | 检查本地内存使用,优化本地代码 | > **注意**:以上错误类型中,前6种较为常见,后几种在特定场景下出现[^1][^4]。 --- ### 相关问题 1. 如何诊断Java堆内存溢出(Java heap space)? 2. 元空间(Metaspace)和永久代(PermGen)的区别是什么? 3. 如何监控和调整直接内存(Direct Buffer Memory)的使用? 4. 为什么GC开销超过限制(GC Overhead limit exceeded)通常意味着内存泄漏? 5. 如何避免“无法创建新的本地线程(unable to create new native thread)”错误? [^1]: 类加载的过程,就是把 class 文件装载到 JVM 内存中,装载完成以后就会得到一个 Class 对象,我们就可以使用 new 关键字来实例化这个对象。 [^4]: 对象创建在虚拟机中是非常频繁的行为,即使是仅仅修改一个指针所指向的位置,在并发情况下也并不是线程安全的,可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况。 CAS机制。</think>### Java中OutOfMemoryError的分类及含义 `OutOfMemoryError`是JVM无法满足内存分配请求时抛出的严重错误,属于`java.lang.VirtualMachineError`的子类。根据内存区域的不同,主要分为以下类型: #### 1. **Java堆空间溢出** `java.lang.OutOfMemoryError: Java heap space` - **触发场景**:对象数量超过堆内存容量(`-Xmx`设置的最大堆大小) - **典型原因**: - 内存泄漏(对象被意外持有无法回收) - 超大对象分配(如大数组) - **示例代码**: ```java // 持续创建大对象导致堆溢出 List<byte[]> list = new ArrayList<>(); while(true) { list.add(new byte[1024 * 1024]); // 每次分配1MB } ``` #### 2. **元空间溢出** `java.lang.OutOfMemoryError: Metaspace` - **触发场景**:加载的类元数据超出元空间容量(Java 8+取代永久代) - **典型原因**: - 动态生成大量类(如CGLib代理) - 类加载器泄漏 - **配置参数**:`-XX:MaxMetaspaceSize=256m` #### 3. **直接内存溢出** `java.lang.OutOfMemoryError: Direct buffer memory` - **触发场景**:NIO直接缓冲区(`ByteBuffer.allocateDirect()`)超出`-XX:MaxDirectMemorySize` - **典型原因**: - 未及时释放直接缓冲区 - 频繁使用堆外内存操作(如Netty、Mina) - **特征**:堆内存正常但物理内存耗尽 #### 4. **GC开销超限** `java.lang.OutOfMemoryError: GC Overhead limit exceeded` - **触发场景**:JVM花费超过98%时间做GC但回收率低于2% - **典型原因**: - 堆内存过小 - 存在大量短命对象 - **解决方案**:调整GC策略或增大堆内存[^5] #### 5. **无法创建线程** `java.lang.OutOfMemoryError: unable to create new native thread` - **触发场景**:线程数超过操作系统限制 - **典型原因**: - 线程池配置过大(`Executors.newCachedThreadPool()`滥用)[^3] - Linux系统`ulimit -u`限制 - **示例**:递归创建线程不释放 #### 6. **请求数组过大** `java.lang.OutOfMemoryError: Requested array size exceeds VM limit` - **触发场景**:尝试分配超过`Integer.MAX_VALUE - 8`的数组 - **典型代码**: ```java int[] arr = new int[Integer.MAX_VALUE]; // 数组大小约2^31个元素 ``` #### 7. **代码缓存区溢出** `java.lang.OutOfMemoryError: CodeCache is full` - **触发场景**:JIT编译的本地代码超出代码缓存区 - **配置参数**:`-XX:ReservedCodeCacheSize=128m` ### 诊断与解决流程 ```mermaid graph TD A[OutOfMemoryError] --> B{分析错误类型} B -->|Heap space| C[检查堆内存泄漏] B -->|Metaspace| D[检查动态类生成] B -->|Direct buffer| E[检查NIO缓存释放] B -->|GC Overhead| F[调整GC策略或堆大小] C --> G[使用MAT分析堆转储] D --> H[限制CGLib代理] E --> I[显式调用Cleaner] F --> J[切换CMS/G1收集器][^5] ``` ### 关键区别总结 | 错误类型 | 内存区域 | 可恢复性 | 常见触发场景 | |------------------------|---------------|----------|---------------------------| | **Java heap space** | 堆内存 | ❌ | 内存泄漏/大对象 | | **Metaspace** | 元数据区 | ❌ | 动态类加载 | | **Direct buffer** | 堆外内存 | ❌ | NIO缓冲区未释放 | | **GC Overhead** | GC系统 | ❌ | 垃圾收集效率低下 | | **unable create thread**| 系统资源 | ❌ | 线程数超限 | > **注意**:所有`OutOfMemoryError`都表示**JVM级不可恢复错误**,需通过调整JVM参数或修复代码逻辑解决[^1][^4]。 --- ### 相关问题 1. 如何生成和分析堆转储文件(Heap Dump)诊断`Java heap space`错误? 2. 元空间(Metaspace)和永久代(PermGen)的内存管理有何本质区别? 3. 为什么直接内存(Direct Buffer)溢出时堆内存使用率可能显示正常? 4. 如何合理配置线程池避免`unable to create new native thread`错误?[^3] 5. G1收集器如何降低`GC Overhead limit exceeded`的发生概率?[^5] [^1]: 类加载的过程,就是把 class 文件装载到 JVM 内存中,装载完成以后就会得到一个 Class 对象。 [^3]: 主要有newFixedThreadPool,newCachedThreadPool,newSingleThreadExecutor,newScheduledThreadPool四种线程池。 [^4]: 对象创建在虚拟机中是非常频繁的行为,即使是仅仅修改一个指针所指向的位置,在并发情况下也并不是线程安全的。 [^5]: CMS收集器是以获取最短回收停顿时间为目标的收集器,它在垃圾收集时使得用户线程和 GC 线程并发执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值