任务堆积导致 OOM(内存溢出)
文章目录
- 任务堆积导致 OOM(内存溢出)
- 问题描述
- 错误实现
- 正确实现
任务堆积导致 OOM(内存溢出)
问题描述
使用无界队列(如 LinkedBlockingQueue
)的线程池,在任务提交速率远高于处理速率时,队列无限堆积,最终导致内存溢出(OOM)。
错误实现
public class QueueOOMDemo {
// 静态集合防止GC回收内存(加速OOM)
private static final List<byte[]> memoryHolder = new ArrayList<>();
public static void main(String[] args) {
// 使用无界队列(快速堆积大对象)
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>());
// 提交大量任务,每个任务分配2MB内存(加速内存消耗)
for (int i = 0; i < 20000; i++) {
executor.execute(() -> {
// 分配2MB内存,并存入静态集合防止GC回收
byte[] chunk = new byte[2 * 1024 * 1024];
synchronized (memoryHolder) {
memoryHolder.add(chunk);
}
// 模拟任务耗时
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
}
运行结果
Exception in thread "pool-1-thread-2" Exception in thread "pool-1-thread-3" Exception in thread "pool-1-thread-1" Exception in thread "pool-1-thread-4" Exception in thread "pool-1-thread-6" Exception in thread "pool-1-thread-5" Exception in thread "pool-1-thread-7" Exception in thread "pool-1-thread-8" java.lang.OutOfMemoryError: Java heap space
at com.szkingdom.example.thread01.QueueOOMDemo.lambda$main$0(QueueOOMDemo.java:30)
at com.szkingdom.example.thread01.QueueOOMDemo$$Lambda$1/897913732.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "pool-1-thread-12" java.lang.OutOfMemoryError: Java heap space
Exception in thread "pool-1-thread-13" java.lang.OutOfMemoryError: Java heap space
问题分析
- 无界队列风险:默认队列容量为 `Integer.MAX_VALUE`,任务无限堆积,直至内存耗尽。
- OOM类型:通常为 `GC overhead limit exceeded` 或 `Java heap space`。
正确实现
- 限制队列容量:使用有界队列,防止任务无限堆积。
- 合理拒绝策略:任务满时触发拒绝策略,避免内存溢出。
public class QueueOOMFixedDemo {
public static void main(String[] args) {
// 使用有界队列(容量1000)和拒绝策略
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1000), // 队列容量1000
new ThreadPoolExecutor.AbortPolicy()
);
try {
for (int i = 0; i < 20000; i++) {
executor.execute(() -> {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
} catch (RejectedExecutionException e) {
System.err.println("任务被拒绝,已提交任务数:" + executor.getTaskCount());
}
executor.shutdown();
}
}
运行结果
任务被拒绝,已提交任务数:1004