小谈Java内存泄露

今天一哥们接到了淘宝的电话面试,其中一题问到Java的内存泄露。面试之后,我们在群里稍微讨论了一下,结果他认为我的看法不对,让我再去看看资料。在网上看了一些资料,并且翻阅了《疯狂Java》中的“内存管理”章节之后,我反而觉得我的看法更加正确了。

我的看法是:对于一般的强引用,并不需要特别刻意地去处理它,GC已经能够做得很好了,需要注意的是Java的类定义中隐藏的对象引用

对于Java的内存泄露,下面这个链接可以讲解:

http://www.cnblogs.com/qq78292959/archive/2011/07/25/2116123.html

总结书上和网上的说法,对于Java的内存泄露,有以下几个方面是需要注意的:

1. 对象游离

即对象实例的实际生命周期超出了程序需要它的时间长度。通常,有几种情况会导致这种内存泄露:

  1. 循环体中实例化的对象
  2. 在方法开始处实例化且很少用到的对象

上述两种情况,资料上处理它们的办法都是在最后一次使用后就用引用它们的变量置为null。

但是,在我看来,这多少有点小题大做。对于第1种情况,如果循环体中实例化的对象没有被循环体外部的变量所引用,那么一遍循环结束时,由于它是局部变量,即使不置为null,GC也自然会将其回收。而对于第2种情况,除非方法中是一些非常耗时(比如调用了经常会被挂起方法)、耗内存(比如读取了大规模的数据)的操作,否则一个设计良好的方法顶多不超过300行——甚至300行都太多了,有什么必要在中途就将变量置为null呢?

终上所述,对于所谓“对象游离”这种可能造成内存泄露的情形,实际上只有当程序运行在内存有严格限制的情况下,才有必要专门对它进行处理。换句话说,它并不是造成Java内存泄露的主要原因。

2. 静态变量

静态变量的厉害之处在于它属于类本身而不属于实例,因此即使代码中没有强引用显式地调用某类的静态变量,当该类出现在代码中时,该类中的所有静态变量就已经事实上地被强引用了。

如前所述,Java的内存泄露中,与类似“对象游离”这种有显式强引用的相比,隐藏在数据结构中的隐式强引用才更应该得到注意。

参见如下链接中的单例模式例子:

http://www.blogjava.net/killme2008/archive/2007/11/11/159786.html

单例模式实际上就是静态变量的一个非常重要的应用,但是它也隐藏着很大的内存泄露的风险。即当类中的静态变量所引用的实例所占用的内存过大时,如果不及时将其置为null,那么它将会是一个持续整个程序生命周期的强引用,即这块内存会被一直占用到程序结束。

3. 泛型集合

Java的泛型集合给编程带来了很大的便利,与它相比,C++的STL简直弱爆了。但是,给我的感觉似乎JVM的设计团队与JDK 1.4以后泛型集合的设计团队并不是同一个,因为该JVM的精致(尽管也有很多问题,相对而言)相比,泛型集合在设计时其实就为Java的内存泄露埋下了巨大的隐患,而这也是被诸多资料轻描淡写就略过的。

以ArrayList为例,它的remove方法的实现方式,实际上并不是将目标对象从列表数组中去除,而是将它移到列表尾部,再将size减1,如此一来,数组本来除了顺序之外并没有改变,只是遍历时需要访问的对象抛开了目标对象而已。如此一来,本该被释放掉的对象实际上仍然存在于内存之中。

类似的还有HashMap,从哈希表中删除对象并不会立即解除对该对象的引用,因而GC也就无法及时释放掉它。

可见,一旦泛型集合中隐藏的对象实例多起来得不到释放,才会一瞬间对内存造成巨大的负担,并且由于集合一般都会较长时间存在于程序之中,由泛型集合造成的Java内存泄露理论上应该比前二者更为严重。


相比C++而言,Java由于GC的工作机制(无引用的对象直接释放)和finalize方法不被推荐,需要手动处理内存泄露的地方实际上很少,上述几个方面都是在极端情况下(内存限制或超大数据量)才会对程序造成严重的内存泄露影响,甚至造成程序崩溃。但理论上讲这些都不是没有可能的,尤其是当今数据量越来越大的情况下,而且内存泄露的问题又是面试经常考的题目。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值