养成良好编程习惯可从源头防止内存泄漏:资源操作须用try-with-resources;静态引用应可控,优先选WeakHashMap;监听器需及时注销;ThreadLocal必须finally中remove();缓存集合要有明确清理机制。

养成良好的编程习惯,比依赖工具更能从源头掐断内存泄漏。关键不是记住多少规则,而是让这些意识自然融入日常编码节奏中。
资源操作必须配对关闭
凡是实现了 AutoCloseable 的资源(如 FileInputStream、Connection、ResultSet、Socket),绝不裸写 new + close()。哪怕只有一行读取逻辑,也要用 try-with-resources 包裹。
- 即使发生异常,资源也能确保释放,避免因流程跳转遗漏 close()
- 不要在 finally 块里手动 close()——容易重复 close 或忽略 close 异常
- 自定义资源类也应实现 AutoCloseable,并在 close() 中释放所有持有的底层资源
静态引用要“看得见、管得住”
static 不是便利贴,而是永久胶水。只要一个 static Map 或 static List 持有业务对象,那个对象就几乎不可能被回收。
- 问自己:这个静态容器真需要跨整个应用生命周期存在吗?能不能改成局部变量或作用域更小的实例变量?
- 如果必须用静态缓存,优先选 WeakHashMap 或带过期策略的 Caffeine,而不是 HashMap
- 所有静态集合都要配套提供 clear() 或 remove() 方法,并在业务逻辑明确结束时调用
监听器和回调必须“注册即承诺注销”
注册监听器的本质,是建立一条从组件到监听器的强引用链。不注销,这条链就一直挂着。
立即学习“Java免费学习笔记(深入)”;
- GUI 或 Android 开发中,在 onDestroy() / dispose() / close() 中统一反注册,别靠“应该没人用了”侥幸
- 使用 lambda 注册监听器时尤其小心——它隐式捕获了 this 和外部变量,可能延长整个对象图的存活
- 考虑用弱监听器模式(WeakReference 封装 listener)或事件总线框架(如 EventBus 的 sticky 机制需谨慎)
ThreadLocal 必须显式 remove()
在线程池场景下,ThreadLocal 是高频泄漏点。线程复用后,前一个请求存的数据还留在 ThreadLocal 里,拖着整个上下文不释放。
- 永远在 finally 块中调用 threadLocal.remove(),而不是 set(null)
- 避免在 ThreadLocal 中存大对象(如完整 DTO、Session 上下文),优先存 ID 或轻量标识
- 若用 Spring 的 RequestScope Bean,本质也是基于 ThreadLocal,需确认其销毁时机是否匹配
缓存与集合操作保持“有进有出”
缓存不是垃圾桶,集合不是保险柜。任何长期存在的容器,都得有明确的退出机制。
- 不用“先放进去,后面再清理”的想法——后面往往不会来
- 用 LinkedHashMap 实现 LRU 缓存时,重写 removeEldestEntry();用 Caffeine 时配置 maximumSize() 和 expireAfterWrite()
- 遍历集合删除元素,用迭代器 remove(),别边遍历边调用集合的 remove(),也别只设引用为 null



















