Java内存泄漏与内存溢出的实际场景
一、内存泄漏(Memory Leak)实际场景
内存泄漏是指对象不再被使用,但无法被垃圾回收器回收,导致内存被无效占用。
1. 集合类泄漏
java
复制
下载
// 案例:静态集合不断添加元素且不清理 public class CollectionLeak { private static List<Object> list = new ArrayList<>(); public void addData(Object data) { list.add(data); // 数据只增不减 } }
场景:缓存系统未设置上限或清理策略,随着时间推移耗尽内存。
2. 监听器未注销
java
复制
下载
// 案例:添加监听器后未移除 public class ListenerLeak { private List<EventListener> listeners = new ArrayList<>(); public void addListener(EventListener listener) { listeners.add(listener); } // 缺少removeListener方法 }
场景:GUI程序中反复打开/关闭窗口,导致旧窗口监听器堆积。
3. 线程池未关闭
java
复制
下载
ExecutorService executor = Executors.newFixedThreadPool(5); executor.submit(() -> {...}); // 忘记调用executor.shutdown()
场景:Web应用中每次请求都创建新线程池,线程资源无法回收。
4. 数据库连接未关闭
java
复制
下载
try { Connection conn = DriverManager.getConnection(url); // 使用后忘记conn.close() } catch (SQLException e) { e.printStackTrace(); }
场景:高频数据库操作导致连接池耗尽。
二、内存溢出(OOM)实际场景
内存溢出是指JVM内存不足以分配新对象,抛出OutOfMemoryError。
1. 堆内存溢出(Java heap space)
java
复制
下载
// 案例:加载超大文件到内存 public void loadHugeFile(String path) throws IOException { byte[] data = Files.readAllBytes(Paths.get(path)); // 文件大小超过堆容量 }
典型报错:java.lang.OutOfMemoryError: Java heap space
2. 方法区溢出(Metaspace/PermGen)
java
复制
下载
// 案例:动态生成大量类 public class ClassGenerator { public void generate() { while(true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(MyClass.class); enhancer.create(); // 不断生成代理类 } } }
典型报错:java.lang.OutOfMemoryError: Metaspace
(或PermGen space)
3. 栈溢出(StackOverflowError)
java
复制
下载
// 案例:无限递归 public class StackOverflowDemo { public void recursive() { recursive(); // 无限调用 } }
典型报错:java.lang.StackOverflowError
4. 直接内存溢出(Direct buffer memory)
java
复制
下载
// 案例:NIO操作分配过多直接内存 ByteBuffer.allocateDirect(1024 * 1024 * 500); // 分配500MB直接内存
典型报错:java.lang.OutOfMemoryError: Direct buffer memory
三、诊断与排查方法
1. 内存泄漏排
# 1. 获取堆转储文件 jmap -dump:format=b,file=heap.hprof <pid> # 2. 使用MAT分析支配树(Dominator Tree) # 查找占用内存最大的对象链条
2. 内存溢出排查
# 1. 查看各区内存使用 jstat -gc <pid> 1000 # 2. 调整参数获取更多信息 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump.hprof
四、预防措施对比
问题类型 | 预防措施 |
---|---|
集合泄漏 | 使用WeakHashMap、定期清理、设置大小限制 |
监听器泄漏 | 实现注销机制、使用弱引用 |
连接泄漏 | 使用try-with-resources、连接池 |
堆溢出 | 增加-Xmx、优化数据结构、分页处理大数据 |
方法区溢出 | 增加-XX:MaxMetaspaceSize、避免动态生成过多类 |
栈溢出 | 优化递归为迭代、增加-Xss |
直接内存溢出 | 合理使用ByteBuffer、增加-XX:MaxDirectMemorySize |
五、典型案例分析
案例1:电商系统订单缓存泄漏
现象:每天凌晨重启才能恢复正常运行
原因:使用HashMap
缓存所有历史订单,未设置过期策略
解决:改用Caffeine
缓存,配置最大条目和过期时间
案例2:报表导出OOM
现象:导出大数据量报表时崩溃
原因:将全部查询结果加载到ArrayList
解决:改为流式处理,分批次读写
案例3:Android图片加载泄漏
现象:App长时间使用后卡顿
原因:ImageView
持有Bitmap
引用,Activity销毁未清理
解决:使用WeakReference
或Glide
等专业图片库
六、工具推荐
-
内存分析:Eclipse MAT、JVisualVM
-
实时监控:Arthas、Prometheus + Grafana
-
压力测试:JMeter、Gatling
-
代码扫描:FindBugs、SonarQube
通过理解这些实际场景,可以在开发过程中主动避免常见的内存问题,提高系统稳定性。