强制系统垃圾回收的两种方式:
调用System类的gc()静态方法:System.gc()。
调用Runtime对象的gc()实例方法:Runtime.getRuntime.gc()。
强制垃圾回收机制调用可恢复对象的finalize()方法以及通知系统进行资源清理
调用System类的runFinalization()静态方法:System.runFinalization()。
调用Runtime对象的runFinalization()实例方法:Runtime.getRuntime.runFinalization()。
Java对对象的4中引用方式
1、强引用(StrongReference)
StrongReference是Java程序中最常见的引用方式。程序创建一个对象,并把这个对象赋给一个引用变量,程序通过该引用变量来操作实际的对象,当一个对象被一个或一个以上的引用变量所引用时,它处于可达状态,不可能被系统垃圾回收机制回收。
@Test
public void strongReference() {
Object referent = new Object();
/**
* 通过赋值创建 StrongReference
*/
Object strongReference = referent;
assertSame(referent, strongReference);
referent = null;
System.gc();
/**
* StrongReference 在 GC 后不会被回收
*/
assertNotNull(strongReference);
}
2、软引用(SoftReference,对应JDK中java.lang.ref.SoftReference)
软引用需要通过SoftReference类来实现,当一个对象只有软引用时,它有可能被垃圾回收机制回收。对于只有软引用的对象而言,当系统内存空间足够时,它不会被系统回收,程序也可以使用该对象;当系统内存空间不足时,系统可能会回收它。软引用通常用于对内存敏感的程序中。
@Test
public void softReference() {
Object referent = new Object();
SoftReference<Object> softRerference = new SoftReference<Object>(referent);
assertNotNull(softRerference.get());
referent = null;
System.gc();
/**
* soft references 只有在 jvm OutOfMemory 之前才会被回收, 所以它非常适合缓存应用
*/
assertNotNull(softRerference.get());
}
3、WeakReference(弱引用,对应JDK中java.lang.ref.WeakReference)
弱引用通过WeakReference类实现,弱引用和软引用很像,但弱引用的引用级别更低。对于只有弱引用的对象而言,当系统回收机制运行时,不管系统内存是否足够,总会回收该对象占用的内存。
@Test
public void weakReference() {
Object referent = new Object();
WeakReference<Object> weakRerference = new WeakReference<Object>(referent);
assertSame(referent, weakRerference.get());
referent = null;
System.gc();
/**
* 一旦没有指向 referent 的强引用, weak reference 在 GC 后会被自动回收
*/
assertNull(weakRerference.get());
}
4、WeakHashMap实现类
WeakHashMap与HashMap的用法基本相似。与HashMap的区别在于,HashMap的key保留了对实际的强引用,意味着该HashMap不会被销毁,其所有key所引用得对象就不会被回收,HashMap也不会自动删除这些key所对应的key-value对;但WeakHashMap的key只保留实际对象的弱引用,这些key所引用的对象可能被垃圾回收,WeakHashMap也可能自动删除这些key对应的key-value对。
public class WeakHashMapTest{
public static void main(){
WeakHashMap whm = new WeakHashMap();
// 将WeakHashMap中添加三个key-value对,
// 三个key都是匿名字符串对象(没有其他引用)
whm.put(new String("语文") , new String("良好"));
whm.put(new String("数学") , new String("及格"));
whm.put(new String("英文") , new String("中等"));
//将 WeakHashMap中添加一个key-value对,
// 该key是一个系统缓存的字符串对象。
whm.put("java" , new String("中等")); // ①
// 输出whm对象,将看到4个key-value对。
System.out.println(whm);
// 通知系统立即进行垃圾回收
System.gc();
System.runFinalization();
// 通常情况下,将只看到一个key-value对。
System.out.println(whm);
}
}
编译、运行上面程序,看到如下运行如果:
{英文=中等, java=中等, 数学=及格, 语文=良好}
{java=中等}
当系统进行垃圾回收时,删除了WeakHashMap对象的前三个key-value对。这是因为添加前三个key-value对(粗体字部分)时,这三个key都是匿名的字符串对象,WeakHashMap只保留了对它们的弱引用,这个垃圾回收时会自动删除这三个key-value对。
WeakHashMap对象中第4个组key-value对的key是一个字符串直接量,(系统会自动保留对该字符串对象的强引用),所以垃圾回收是不会回收它。
注:如果需要使用WeakHashMap的key来保留对象的弱引用,则不要让该key所引用的对象具有任何强引用,否则将失去使用WeakHashMap的意义。
5、PhantomReference (虚引用,对于JDK中java.lang.ref.PhantomReference )
通过PhantomReference类实现,徐引用类似完全没有引用。虚引用对对象本身没有太大影响,对象甚至感觉不到虚引用的存在。如果一个对象只有一个虚引用时,它和没有引用的效果大致相同。因为它的 get() 方法永远返回 null。
@Test
public void phantomReferenceAlwaysNull() {
Object referent = new Object();
PhantomReference<Object> phantomReference = new PhantomReference<Object>(referent, new ReferenceQueue<Object>());
/**
* phantom reference 的 get 方法永远返回 null
*/
assertNull(phantomReference.get());
}
注:SoftReference、WeakReference以及PhantomReference均包含一个get()方法,用于获取被它们所引用的对象。
6、ReferenceQueue(引用队列,对于JDK中java.lang.ref.ReferenceQueue)
用于保存被回收后对象的引用。当联合使用软引用、弱引用和引用队列时,系统在回收被引用的对象之后,将把被回收对象对应的引用添加到关联的引用队列中。与软引用和弱引用不同的是,虚引用在对象释放之前,将把它对应的虚引用添加到它关联的引用队列中,这使得可以在对象在被回收之前采取行动。程序可以检查与虚引用关联的引用队列中是否已经包含了该虚引用,从而了解虚引用所引用的对象是否即将被回收。
public class PhantomReferenceTest
{
public static void main(String[] args)
throws Exception
{
// 创建一个字符串对象
String str = new String("疯狂Java讲义");
// 创建一个引用队列
ReferenceQueue rq = new ReferenceQueue();
// 创建一个虚引用,让此虚引用引用到"疯狂Java讲义"字符串
PhantomReference pr = new PhantomReference (str , rq);
// 切断str引用和"疯狂Java讲义"字符串之间的引用
str = null;
// 取出虚引用所引用的对象,并不能通过虚引用获取被引用的对象,所以此处输出null
System.out.println(pr.get()); // ①
// 强制垃圾回收
System.gc();
System.runFinalization();
// 垃圾回收之后,虚引用将被放入引用队列中
// 取出引用队列中最先进入队列中的引用与pr进行比较
System.out.println(rq.poll() == pr); // ②
}
}
因为系统无法通过虚引用获得被引用的对象,多以执行①处的输出语句时,程序输出null。当程序强制垃圾回收回收后,只有虚引用引用的字符串对象将被垃圾回收,当被引用的对象被回收后,对应的虚引用将被添加到关联的引用队列中,因而将在②代码处看到输出true。
由于垃圾回收的不确定性,当程序希望从软、弱引用中获取被引用对象时,可能这个对象已经被释放了。如果程序需要使用被引用对象,则必须被重新创建该对象。这个过程可以采用两种方式:
// 取出弱引用所引用的对象
obj = wr.get();
// 如果取出的对象为null
if(obj == null){
// 重新创建一个新的对象,再次让弱引用去引用该对象
wr = new WeakReference(recreateIt()) ;
// 取出弱引用所引用的对象,将其赋给obj变量
obj = wr.get() ;
... // 操作obj对象
// 再次切断obj和对象之间的关联
}
// 再次切断obj和对象之间的关联
obj = null ;
recreateIt()表示创建一个obj对象,这段代码存在一个问题,就是垃圾回收的不确定性,可能会导致新创建的弱引用再次被回收,从而获取到的obj仍然是null。另一种取出方式避免了这个问题:
// 取出弱引用所引用的对象
obj = wr.get();
// 如果取出的对象为null
if(obj == null){
// 重新创建一个新的对象,并使用强引用来引用它
obj = recreateIt() ;
// 取出弱引用所引用的对象,将其赋给obj变量
wr = new WeakReference(obj) ;
... // 操作obj对象
// 再次切断obj和对象之间的关联
}
// 再次切断obj和对象之间的关联
obj = null ;
7、PhantomReference vs WeakReference
PhantomReference 有两个好处:
1、它可以让我们准确地知道对象何时被从内存中删除, 这个特性可以被用于一些特殊的需求中(例如 Distributed GC, XWork 和 google-guice 中也使用 PhantomReference 做了一些清理性工作)。
2、它可以避免 finalization 带来的一些根本性问题, 上文提到 PhantomReference 的唯一作用就是跟踪 referent 何时被 enqueue 到 ReferenceQueue 中, 但是 WeakReference 也有对应的功能, 两者的区别到底在哪呢 ?
这就要说到 Object 的 finalize 方法, 此方法将在 gc 执行前被调用, 如果某个对象重载了 finalize 方法并故意在方法内创建本身的强引用, 这将导致这一轮的 GC 无法回收这个对象并有可能引起任意次 GC, 最后的结果就是明明 JVM 内有很多 Garbage 却 OutOfMemory, 使用 PhantomReference 就可以避免这个问题, 因为 PhantomReference 是在 finalize 方法执行后回收的,也就意味着此时已经不可能拿到原来的引用, 也就不会出现上述问题, 当然这是一个很极端的例子, 一般不会出现。
参考资料:《疯狂Java讲义(第3版)》
参考作者:舞熊科技 - 文章链接
转发申明:我们不占有不侵权,我们只是好文的搬运工!转发请带上原文申明。
网友评论