这段时间在看《深入java虚拟机》,在讲GC的时候提到了无意识对象保持(unintentional object retention)。
1、Java存在内存泄漏(memory leak)吗?是的,而且很隐蔽,不容易被发现。
2、不是说java的GC会自动回收内存吗?是的,但是GC回收的只是垃圾内存,也就是没有被其它“存活引用”引用着的引用。应用程序通过某条路径可以到达的引用所指向的内存是不能也不应该被回收的。
3、什么叫无意识对象保持?就是说你以为某个对象在使用过后其内存很快就会被GC回收,但是由于程序中某些数据结构持有该对象的引用,使GC无法及时回收该对象,直至上述数据结构被销毁。
4、通常什么情况下容易出现无意识对象保持呢?当你需要自己维护内存的时候或者使用映射 数据结构的时候。前者的一个例子就是一个Stack的实现。如果Pop的时候没有将相应的引用置空,那么被弹出的引用指向的对象是不会被回收的,因为它仍然被栈的引用着。这种情况会一直持续到该单元被重新赋值。(详见 《Effecitve Java 》item 5)。后者的一个例子就是Hashmap了。Hashmap中,一个entry的生命周期应该跟Key是保持一致的,也就是说,当key为null时,Hashmap应该把对应的entry删除掉。但实际上Hashmap并不会这样做,因为当key在某个时刻变成null的时候,并没有通知Hashmap,因此Hashmap也就无从下手了。我们在使用Hashmap的时候,当key为null,但是我们没有显式调用remove方法删除对应的项,那么就会出现无意识对象保持了。
5、出现无意识对象保持会导致什么后果呢?明显啦,内存泄漏嘛!堆的使用率不断增长,垃圾回收变得越来越频繁(要为新的对象争取空间啊),应用程序的性能也就随之下降了。搞不好,还会弄个OutOfMemeory异常出来,挂了!
6、那怎么办啊?要解决这个问题肯定要先找出问题的源头在哪。究竟哪里导致了无意识对象保持呢?这个靠猜测是不行的了,使用工具吧。最原始的工具就是jvm了,在启动jvm的时候加上参数 -Xloggc:filename,就可以得到一个关于GC和堆使用情况的报告了。如果加上 -Xrunhprof<option> 的话,可以得到程序的运行情况的描述。当然,如果你是超人的话,用记事本打开上述两个文件,看看里面的内容可能也会瞅出个端倪出来。但是一般人是不可能的啦,用HPjmeter 和 HPjtune吧,HP公司免费提供的两个小巧玲珑但是功能强大的java监测工具。它们可以将上述的两个文件的内容图形化地表现出来,并作出了相关的统计。具体的介绍可以参看 http://www.javaperformancetuning.com/tools/hpjmeter/index.shtml 和 http://www.javaperformancetuning.com/tools/hpjtune/index.shtml 。
7、问题根源找到了,怎么解决它呢?在程序中显示将不再使用的引用显示置空是一个办法。这对于第一种情况是十分适用的。但这个方法并不是普遍适用的,Bloch说这样会把代码弄得很乱,而且会降低程序的性能(可能是导致出现NullPointerException吧,异常是挺耗资源的)。我们可以重用一个本来已经包含对象的的引用变量或者让这个变量结束其生命周期。对于第二种情况,java 1.2提出通过Reference对象进行解决,主要就是使用SoftReference 和 WeakReference对象。jvm会在抛出OutOfMemoryError之前清除SoftReference对象,以回收内存空间。对于WeakReference对象,jvm则会在监测到某个对象是WeakReference对象时马上进行清除。使用这两种对象,我们可以实现一个廉价的缓存和建立规范映射。WeakHashMap就是使用WeakReference实现的一个HashMap,以解决无意识对象保持的问题。
1、Java存在内存泄漏(memory leak)吗?是的,而且很隐蔽,不容易被发现。
2、不是说java的GC会自动回收内存吗?是的,但是GC回收的只是垃圾内存,也就是没有被其它“存活引用”引用着的引用。应用程序通过某条路径可以到达的引用所指向的内存是不能也不应该被回收的。
3、什么叫无意识对象保持?就是说你以为某个对象在使用过后其内存很快就会被GC回收,但是由于程序中某些数据结构持有该对象的引用,使GC无法及时回收该对象,直至上述数据结构被销毁。
4、通常什么情况下容易出现无意识对象保持呢?当你需要自己维护内存的时候或者使用映射 数据结构的时候。前者的一个例子就是一个Stack的实现。如果Pop的时候没有将相应的引用置空,那么被弹出的引用指向的对象是不会被回收的,因为它仍然被栈的引用着。这种情况会一直持续到该单元被重新赋值。(详见 《Effecitve Java 》item 5)。后者的一个例子就是Hashmap了。Hashmap中,一个entry的生命周期应该跟Key是保持一致的,也就是说,当key为null时,Hashmap应该把对应的entry删除掉。但实际上Hashmap并不会这样做,因为当key在某个时刻变成null的时候,并没有通知Hashmap,因此Hashmap也就无从下手了。我们在使用Hashmap的时候,当key为null,但是我们没有显式调用remove方法删除对应的项,那么就会出现无意识对象保持了。
5、出现无意识对象保持会导致什么后果呢?明显啦,内存泄漏嘛!堆的使用率不断增长,垃圾回收变得越来越频繁(要为新的对象争取空间啊),应用程序的性能也就随之下降了。搞不好,还会弄个OutOfMemeory异常出来,挂了!
6、那怎么办啊?要解决这个问题肯定要先找出问题的源头在哪。究竟哪里导致了无意识对象保持呢?这个靠猜测是不行的了,使用工具吧。最原始的工具就是jvm了,在启动jvm的时候加上参数 -Xloggc:filename,就可以得到一个关于GC和堆使用情况的报告了。如果加上 -Xrunhprof<option> 的话,可以得到程序的运行情况的描述。当然,如果你是超人的话,用记事本打开上述两个文件,看看里面的内容可能也会瞅出个端倪出来。但是一般人是不可能的啦,用HPjmeter 和 HPjtune吧,HP公司免费提供的两个小巧玲珑但是功能强大的java监测工具。它们可以将上述的两个文件的内容图形化地表现出来,并作出了相关的统计。具体的介绍可以参看 http://www.javaperformancetuning.com/tools/hpjmeter/index.shtml 和 http://www.javaperformancetuning.com/tools/hpjtune/index.shtml 。
7、问题根源找到了,怎么解决它呢?在程序中显示将不再使用的引用显示置空是一个办法。这对于第一种情况是十分适用的。但这个方法并不是普遍适用的,Bloch说这样会把代码弄得很乱,而且会降低程序的性能(可能是导致出现NullPointerException吧,异常是挺耗资源的)。我们可以重用一个本来已经包含对象的的引用变量或者让这个变量结束其生命周期。对于第二种情况,java 1.2提出通过Reference对象进行解决,主要就是使用SoftReference 和 WeakReference对象。jvm会在抛出OutOfMemoryError之前清除SoftReference对象,以回收内存空间。对于WeakReference对象,jvm则会在监测到某个对象是WeakReference对象时马上进行清除。使用这两种对象,我们可以实现一个廉价的缓存和建立规范映射。WeakHashMap就是使用WeakReference实现的一个HashMap,以解决无意识对象保持的问题。