java 的内存泄露 这两天看了一本老书《bitter java》 ,第一次系统地了解了所谓“反模式”。就书的内容来说已 经过于陈旧,书中提到的 magic servlet、复合 jsp 等等反模式已经是早就熟知的编程禁忌, 而如 web 页面不能有太多元素这样的反模式也因为 ajax 的出现 (异步加载) 变的不是那么“反 模式”了,其中又讲述了很多 ejb 的反模式,这些在轻量级框架流行的今天也早已经过时。 不过书中有一个章节倒是挺有价值,讲述的是 java 的内存泄露问题,我认为是我目前读的 关于这方面问题比较有价值的介绍。 网上关于 java 内存泄露的资料都过于玄乎,其实 java 导致内存泄露的原因很明确:长生 命周期的对象持有短生命周期对象的引用就很可能发生内存泄露, 尽管短生命周期对象已经 不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是 java 中内存 泄露的发生场景。作者在书中提到了 3 个场景: 1。流失监听器问题,在 awt、swing 编程中,给组件添加了事件监听器,这些组件的生命周 期如果很长的话,监听器对象将不能被正确回收。关于 GUI 编程我不是很熟悉,这一点存有 疑问,因为显然你触发一个按钮的事件,当然是一直期待同样的行为发生,如果删除了监听 器或者使用弱引用让 JVM 回收不符合业务逻辑和用户体验。 2。集合类,集合类仅仅有添加元素的方法,而没有相应的删除机制,导致内存被占用。这 一点其实也不明确,这个集合类如果仅仅是局部变量,根本不会造成内存泄露,在方法栈退 出后就没有引用了会被 jvm 正常回收。 而如果这个集合类是全局性的变量 (比如类中的静态 属性,全局性的 map 等) ,那么没有相应的删除机制,很可能导致集合所占用的内存只增不 减,因此提供这样的删除机制或者定期清除策略非常必要。 3。单例模式。不正确使用单例模式是引起内存泄露的一个常见问题,单例对象在被初始化 后将在 JVM 的整个生命周期中存在(以静态变量的方式) ,如果单例对象持有外部对象的引 用,那么这个外部对象将不能被 jvm 正常回收,导致内存泄露,考虑下面的例子: class A{ public A(){ B.getInstance().setA(this); } .... } //B 类采用单例模式 class B{ private A a; private static B instance=new B(); public B(){} public static B getInstance(){ return instance; } public void setA(A a){ this.a=a; } //getter... } 显然 B 采用 singleton 模式,他持有一个 A 对象的引用,而这个 A 类的对象将不能被回收。 想象下如果 A 是个比较大的对象或者集合类型会发生什么情况。 上面所讲的这些也启发我们如何去查找内存泄露问题,第一选择当然是利用工具,比如 jprofiler,第二就是在代码复审的时候关注长生命周期对象:全局性的集合、单例模式的使 用、类的 static 变量等等。