大对象与内存泄漏实战指南
一、大对象(Large Object)问题
1.1 定义与识别
- 标准:占堆内存超过单个Region 50%(G1默认Region大小为1-32MB)
- 常见场景:
- 缓存:未分页的数据库查询结果集
- 文件处理:
byte[]一次性加载10GB文件 - 集合类:
ArrayList存储百万级对象
1.2 典型案例与优化
案例1:未分页的缓存设计
// 错误实现:全局缓存无上限
public class ProductCache {
private static Map<Long, Product> cache = new HashMap<>();
public static void loadAll() {
List<Product> products = productDao.findAll(); // 加载10w条记录
products.forEach(p -> cache.put(p.getId(), p));
}
}
问题:
- 老年代被大HashMap占满 → 频繁Full GC
优化方案: - 改用分页缓存 + Guava Cache过期策略
LoadingCache<Long, Product> cache = CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(id -> productDao.getById(id));
案例2:文件处理内存溢出
// 错误:一次性加载大文件
byte[] fileData = Files.readAllBytes(Paths.get("10GB.zip"));
**现象:**OutOfMemoryError: Java heap space
优化方案:
- 流式处理 + 分块读取
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("file.zip"))) {
byte[] buffer = new byte[8192];
while (bis.read(buffer) != -1) {
processChunk(buffer); // 分块处理
}
}
二、内存泄漏(Memory Leak)排查
2.1 高频泄漏场景
| 类型 | 特征 | 检测工具 |
|---|---|---|
| 静态集合泄漏 | static Map长期持有对象 | MAT Dominator Tree |
| 未关闭资源 | InputStream/DirectBuffer未释放 | JFR监控堆外内存 |
| 监听器未注销 | Listener集合只增不减 | Arthas vmtool追踪 |
2.2 动态类生成泄漏
场景:CGLIB代理类堆积
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyService.class);
enhancer.setCallback(new MethodInterceptor() { /* ... */ });
MyService proxy = (MyService) enhancer.create(); // 每次生成新类
}
现象: Metaspace持续增长 → OOM
根因:
- 每个代理类由独立类加载器加载 → 类无法卸载
解决方案: - 限制元空间:-XX:MaxMetaspaceSize=512m
- 使用共享类加载器
2.3 线程局部变量泄漏
public class ThreadLocalLeak {
private static ThreadLocal<User> userHolder = new ThreadLocal<>();
public void setUser(User user) {
userHolder.set(user);
}
// 忘记调用remove()
}
问题: 线程池复用场景下,User对象长期存活
修复:
try {
userHolder.set(user);
// ...业务逻辑
} finally {
userHolder.remove(); // 必须清理
}
三、诊断工具链
3.1 内存分析四步法
1、监控指标:
jstat -gcutil <pid> 1000 # 实时GC统计
jcmd <pid> VM.native_memory # 堆外内存分析
2、堆转储分析:
jmap -dump:live,format=b,file=heap.bin <pid>
3、热点定位:
- MAT:Dominator Tree找最大对象
- JFR:jfr print --events OldObjectSample leak.jfr
4、在线追踪:
arthas $ vmtool --action=getInstances --className java.util.HashMap --limit 10
1140

被折叠的 条评论
为什么被折叠?



