365bet取款要多久-365bet体育365bet官网-365电子游戏

JVM中产生OOM(内存溢出)的8种典型情况及解决方案

JVM中产生OOM(内存溢出)的8种典型情况及解决方案

Java中的OutOfMemoryError(OOM)是当JVM内存不足时抛出的错误。本文将全面剖析JVM中产生OOM的各种情况,包括堆内存溢出、方法区溢出、栈溢出等,并提供详细的诊断方法和解决方案。

一、OOM基础概念

1.1 OOM错误类型

Java中的OOM是java.lang.OutOfMemoryError的子类,常见的有:

Java heap space:堆空间不足

GC Overhead limit exceeded:GC效率低下

PermGen space/Metaspace:方法区溢出

Unable to create new native thread:线程创建失败

Requested array size exceeds VM limit:数组过大

Direct buffer memory:直接内存溢出

Code cache:代码缓存区满

Kill process or sacrifice child:Linux系统级限制

二、堆内存溢出(Java heap space)

2.1 产生原因

当对象需要分配到堆内存时,如果堆内存不足且无法通过GC回收足够空间时抛出。

// 典型示例

java

复制代码

public class HeapOOM {

public static void main(String[] args) {

List list = new ArrayList<>();

while(true) {

list.add(new byte[1024*1024]); // 每次分配1MB

}

}

}

2.2 错误信息

java

复制代码

java.lang.OutOfMemoryError: Java heap space

2.3 解决方案

调整堆大小:

java

复制代码

-Xms256m -Xmx1024m # 初始堆256MB,最大堆1GB

内存分析:

使用jmap获取堆转储:

java

复制代码

jmap -dump:format=b,file=heap.hprof

使用MAT/Eclipse Memory Analyzer分析

代码优化:

避免内存泄漏(如静态集合、未关闭资源)

使用对象池重用对象

三、GC开销超限(GC Overhead limit exceeded)

3.1 产生原因

当JVM花费超过98%的时间进行GC,但只恢复了不到2%的堆空间时抛出。

// 典型场景:创建大量生命周期短的对象

java

复制代码

public class GCOverheadOOM {

public static void main(String[] args) {

Map map = new HashMap<>();

while(true) {

for(int i=0; i<10000; i++) {

map.put(new Key(i), "Value"+i);

}

map.clear(); // 不完全清除

}

}

}

3.2 错误信息

java

复制代码

java.lang.OutOfMemoryError: GC Overhead limit exceeded

3.3 解决方案

增加堆大小:

java

复制代码

-Xmx2g -XX:+UseG1GC

优化GC策略:

对于大量短生命周期对象,使用G1或ZGC

调整新生代大小:

java

复制代码

-XX:NewRatio=2 # 新生代占堆的1/3

代码改进:

减少临时对象创建

使用更高效的数据结构

四、方法区溢出(Metaspace/PermGen)

4.1 产生原因

JDK8前称为PermGen space,JDK8+称为Metaspace,存储类元数据信息。

java

复制代码

// 通过动态生成类填满方法区

public class MetaspaceOOM {

static class OOMObject {}

public static void main(String[] args) {

while(true) {

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(OOMObject.class);

enhancer.setUseCache(false);

enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) ->

methodProxy.invokeSuper(o, objects));

enhancer.create(); // 动态创建类

}

}

}

4.2 错误信息

java

复制代码

// JDK7及之前

java.lang.OutOfMemoryError: PermGen space

// JDK8+

java.lang.OutOfMemoryError: Metaspace

4.3 解决方案

调整Metaspace大小:

java

复制代码

-XX:MaxMetaspaceSize=256m

JDK8前调整PermGen:

java

复制代码

-XX:MaxPermSize=128m

减少动态类生成:

缓存动态代理类

限制反射使用

五、线程栈溢出(Unable to create new native thread)

5.1 产生原因

当创建线程数量超过系统限制时发生。

java

复制代码

public class ThreadOOM {

public static void main(String[] args) {

while(true) {

new Thread(() -> {

try { Thread.sleep(100000); }

catch(InterruptedException e) {}

}).start();

}

}

}

5.2 错误信息

java

复制代码

java.lang.OutOfMemoryError: Unable to create new native thread

5.3 解决方案

减少线程数量:

使用线程池:

java

复制代码

ExecutorService pool = Executors.newFixedThreadPool(100);

调整系统限制:

java

复制代码

ulimit -u # 查看最大线程数

ulimit -u 2048 # 设置最大线程数

减少栈大小:

java

复制代码

-Xss256k # 默认1MB,减少可创建更多线程

六、直接内存溢出(Direct buffer memory)

6.1 产生原因

NIO使用的直接内存(堆外内存)不足时抛出。

java

复制代码

public class DirectMemoryOOM {

public static void main(String[] args) {

// 绕过DirectByteBuffer限制,直接分配内存

List buffers = new ArrayList<>();

while(true) {

buffers.add(ByteBuffer.allocateDirect(1024*1024)); // 1MB

}

}

}

6.2 错误信息

java

复制代码

java.lang.OutOfMemoryError: Direct buffer memory

6.3 解决方案

调整直接内存大小:

java

复制代码

-XX:MaxDirectMemorySize=256m

显式回收:

java

复制代码

((DirectBuffer)buffer).cleaner().clean();

使用池化技术:

Netty的ByteBuf池

七、数组过大溢出(Requested array size exceeds VM limit)

7.1 产生原因

尝试分配超过JVM限制的数组。

java

复制代码

public class ArraySizeOOM {

public static void main(String[] args) {

int[] arr = new int[Integer.MAX_VALUE]; // 约2^31-1个元素

}

}

7.2 错误信息

java

复制代码

java.lang.OutOfMemoryError: Requested array size exceeds VM limit

7.3 解决方案

减小数组大小:

分块处理大数据

使用集合替代:

java

复制代码

List list = new ArrayList<>();

调整数据结构:

使用数据库或文件存储

八、代码缓存溢出(Code cache)

8.1 产生原因

JIT编译的代码填满代码缓存区。

// 通常由大量方法被JIT编译导致

java

复制代码

public class CodeCacheOOM {

public static void main(String[] args) {

// 需要大量方法编译的代码

}

}

8.2 错误信息

java

复制代码

java.lang.OutOfMemoryError: Code cache

8.3 解决方案

增加代码缓存大小:

java

复制代码

-XX:ReservedCodeCacheSize=256m

减少编译阈值:

java

复制代码

-XX:CompileThreshold=10000

关闭分层编译:

java

复制代码

-XX:-TieredCompilation

九、系统级OOM(Kill process or sacrifice child)

9.1 产生原因

Linux系统的OOM Killer终止进程。

java

复制代码

dmesg | grep -i kill

输出示例:

java

复制代码

Out of memory: Kill process 12345 (java) score 999 or sacrifice child

9.2 解决方案

增加系统内存

调整OOM Killer策略:

java

复制代码

echo -17 > /proc/[pid]/oom_adj

限制容器内存(Docker):

java

复制代码

docker run -m 2g my-java-app

十、OOM诊断工具链

|-----------|---------|--------------------------------------------------|

| 工具 | 用途 | 示例命令 |

| jstat | 监控内存和GC | jstat -gcutil 1000 |

| jmap | 堆转储 | jmap -dump:live,format=b,file=heap.hprof |

| jvisualvm | 可视化分析 | 图形化界面 |

| MAT | 内存分析 | 分析hprof文件 |

| jcmd | 多功能工具 | jcmd VM.native_memory |

[OOM诊断工具链]

十一、OOM预防最佳实践

代码层面:

避免内存泄漏(监听器、静态集合)

及时关闭资源(数据库连接、文件流)

使用WeakReference处理缓存

JVM配置:

基础配置示例

java

复制代码

-Xms1g -Xmx2g -XX:MaxMetaspaceSize=256m

-XX:+UseG1GC -XX:MaxGCPauseMillis=200

监控预警:

JMX监控堆内存使用

Prometheus + Grafana监控体系

设置合理的GC日志监控:

java

复制代码

-Xlog:gc*:file=gc.log:time:filecount=5,filesize=10M

十二、总结

OOM类型与对应解决方案速查表:

|-------------------------|------------|----------------------------|

| OOM类型 | 相关内存区域 | 典型解决方案 |

| Java heap space | 堆 | 增大堆,修复内存泄漏 |

| GC Overhead | 堆 | 优化GC策略,减少对象创建 |

| Metaspace/PermGen | 方法区 | 增大Metaspace,减少动态类生成 |

| Unable to create thread | 栈 | 减少线程数,调整-Xss |

| Direct buffer | 直接内存 | 增大MaxDirectMemorySize,显式回收 |

| Array size | 堆 | 减小数组尺寸,分块处理 |

| Code cache | JIT代码缓存 | 增大ReservedCodeCacheSize |

| System OOM | 系统内存 | 增加物理内存,调整OOM Killer |