当前位置: 首页 > news >正文

java对象不被GC回收的情况

不被 GC 回收的内存情况

GC 回收对象的前提是对象不可达(从 GC Roots 出发无法引用到该对象)。以下从多个维度梳理常见的"对象应被回收但未被回收"的情况。


一、对象仍被 GC Roots 引用

这是最根本的原因——对象依然可达,GC 不会回收它。

1. 静态集合引用

public class CacheHolder {// 静态 Map 生命周期与类一致,不会自动释放private static final Map<String, Object> CACHE = new HashMap<>();public void put(String key, Object value) {CACHE.put(key, value); // 对象被静态 Map 持有,永远不会被 GC}
}

要点:静态变量属于 GC Root,它引用的所有对象都不会被回收,直到显式 remove 或置 null

2. 未关闭的资源引用

// 数据库连接、IO 流等未关闭,内部缓冲区被持有
public void query() {Connection conn = dataSource.getConnection();ResultSet rs = conn.createStatement().executeQuery("...");// 忘记 close(),连接池可能仍持有引用,且底层资源不释放
}

3. 监听器/回调未注销

public class EventPublisher {private static final List<Listener> listeners = new ArrayList<>();public void register(Listener l) { listeners.add(l); }// 缺少 unregister() 方法,监听器对象永远被持有
}

二、ThreadLocal 泄漏

// 线程池中线程会复用,ThreadLocal 未清理导致泄漏
public class RequestContext {private static final ThreadLocal<BigObject> CTX = new ThreadLocal<>();public void process() {CTX.set(new BigObject());try {// ... 业务逻辑} finally {CTX.remove();  // 必须显式 remove!否则线程复用时值残留}}
}

原理ThreadLocalMap 的 Key 是弱引用,但 Value 是强引用。Key 被 GC 后 Value 仍存在,成为" Entry",只有 remove() 才能清除。


三、内部类/匿名类持有外部类引用

public class OuterActivity {private byte[] bigData = new byte[100 * 1024 * 1024]; // 100MBpublic void startAsync() {// 匿名内部类隐式持有 OuterActivity.thisnew Thread(new Runnable() {@Overridepublic void run() {// 长时间运行 → bigData 无法释放}}).start();}
}

解决:将内部类改为 static 内部类,或用弱引用包裹外部类。


四、缓存无淘汰策略

// 无容量上限、无过期时间的缓存,只会膨胀
public class LocalCache {private static final ConcurrentHashMap<String, byte[]> CACHE = new ConcurrentHashMap<>();public static void put(String key, byte[] data) {CACHE.put(key, data); // 只进不出,OOM 只是时间问题}
}

解决:使用 WeakHashMap、Guava Cache(设置 maximumSize / expireAfterWrite)或 Caffeine 等带淘汰策略的缓存。


五、类加载器泄漏

// 自定义 ClassLoader 未释放,其加载的所有 Class 及其静态字段均无法回收
public class HotDeploy {private ClassLoader appClassLoader;public void redeploy() {// 每次重新创建 ClassLoader,但旧的未被 GC// → Metaspace OOMappClassLoader = new CustomClassLoader();}
}

典型场景:应用热部署、OSGi 模块、反射框架。旧的 ClassLoader 被某些线程或对象持有引用,导致其加载的类无法卸载。


六、finalize() 方法导致的延迟回收

public class SlowFinalizer {@Overrideprotected void finalize() throws Throwable {// finalize 执行缓慢或阻塞Thread.sleep(60_000); // 60 秒}
}

问题

  • 对象需经过 Finalizer 队列才能回收,至少多经历一次 GC
  • Finalizer 线程优先级低,回收速度跟不上创建速度
  • 如果 finalize() 抛异常或阻塞,对象永远不会被回收
  • JDK 9+ 已标记 finalize()Deprecated,推荐使用 Cleaner

七、字符串常量池 / intern() 滥用

// 大量调用 String.intern(),常量池属于 Metaspace(JDK8-)或堆(JDK7+)
public void process(List<String> strings) {for (String s : strings) {s.intern(); // 每个唯一字符串都驻留在常量池中,无法 GC}
}

八、JNI / Native 内存泄漏

// Direct ByteBuffer 分配的堆外内存,GC 不可见
ByteBuffer buffer = ByteBuffer.allocateDirect(256 * 1024 * 1024);
// 如果没有显式释放或 Cleaner 未触发,直接内存不会回收

注意DirectByteBuffer 的回收依赖于 Cleaner 机制,如果堆内存充裕、GC 不触发,堆外内存可能一直不释放。


九、常见框架中的隐蔽泄漏

场景 说明
Spring Bean 作用域错误 单例 Bean 注入了原型 Bean,原型对象只会创建一次
Log4j/Logback MDC 线程池中未 MDC.clear(),ThreadLocal 泄漏
Hibernate Session 缓存 长事务中 Session 的一级缓存持续积累实体
Ehcache 无过期 缓存未设 TTL,对象堆积
Netty ByteBuf 未调用 release(),引用计数不为零导致泄漏

总结速查表

泄漏类型 根因 排查方式
静态集合 GC Root 持有 MAT → Dominator Tree
ThreadLocal 线程复用未 remove Arthas thread -b / 代码审计
内部类引用 隐式持有所属类 jmap -histo 看类数量
缓存无上限 对象只增不减 监控 Map/Cache size
ClassLoader 类无法卸载 jstat -gcutil 看 Metaspace
finalize 回收延迟 MAT → Finalizer 队列
常量池 intern 滥用 jmap -histo:perf
Native 内存 GC 不可见 NMT(Native Memory Tracking)
框架泄漏 框架特性 框架文档 + 代码审计

核心原则:凡是 GC Root 可达的对象都不会被回收。排查内存泄漏的本质就是——找到那条不该存在的引用链,然后断开它

http://www.jsqmd.com/news/809295/

相关文章:

  • 升级JDK8 spring5的项目至 boot4+jdk26过程记录(一)
  • 别再让 AI 单兵作战了:Claude Code + Codex CLI 组成“AI 开发小队“
  • 2026年树脂混凝土泵站生产厂家权威推荐榜单:树脂混凝土一体化泵站/树脂混凝土沉井泵站/树脂混凝土预制泵站源头厂家精选 - 泵站报价15613348888
  • APK Installer:Windows上安装Android应用的终极指南,简单三步轻松搞定
  • 3步生成专业级代码质量报告:Sonar CNES Report完全指南
  • 告别Bug!用clang-tidy给你的C++代码做个深度体检(附常用检查项配置清单)
  • AI写专著必备攻略:借助AI专著写作工具,3天搞定20万字专著
  • 终极指南:PX4无人机电池健康监测与电量估算算法优化实践
  • STM32F407驱动24C系列EEPROM,一个通用程序搞定从24C01到24C512(附完整KEIL工程)
  • Hive分桶机制应用
  • 收藏必备!小白程序员必看:Agent如何越用越聪明?Hermes技能进化全解析
  • 太赫兹视频SAR极坐标实时成像系统【附代码】
  • 从高德转战Leaflet:一个Vue项目的地图框架迁移实战与避坑指南
  • 北京海斯居科技:昌平正规的空气净化公司 - LYL仔仔
  • 终极指南:如何在Windows上轻松安装APK文件?告别笨重模拟器
  • 在Taotoken模型广场中根据任务需求挑选合适的大模型
  • AI助手如何通过MCP协议自动化操作飞书:feishu-inout工具实战指南
  • 中亚物流通道哪家稳定? - 中媒介
  • 3步免费实现Windows AirPlay 2接收器:打破苹果生态壁垒的终极指南
  • CTFshow密码学入门实战:从凯撒到RSA,手把手带你通关crypto0-13
  • 【23年算法】DBO-DNN多变量回归预测 基于蜣螂算法-深度神经网络多回归时序预测附Matlab代码
  • 众智商学院在行业内排名如何? - 众智商学院官方
  • 从手算到电路:深入剖析计算机中定点数与浮点数的运算实现【硬核解析】
  • 别急着格式化!DiskGenius恢复U盘/移动硬盘RAW格式数据的完整流程
  • 别光会打印星星了!用字符菱形为例,带你玩转C++的控制台‘像素画’
  • YOLOv7 Backbone源码逐层拆解:从CBS到ELAN的工程实现
  • ICC II 物理实现:从 Floorplan 到 Setup 的实战规划
  • 别再被Linux的free命令骗了!手把手教你读懂‘可用内存’和‘实际空闲内存’的区别
  • 承重强不易晃动的猫爬架推荐哪家 - 中媒介
  • 观澜墅二手房价格区间解析:住宅与别墅市场现状 - 品牌2026