对象在内存中的状态
对象在内存中一共有三种状态:
①可达态:在对象创建之后若有一个以上的引用变量引用它就是可达态。程序可以通过引用变量去引用它。
②可恢复态:当对象完全失去引用的时候就进入可恢复态。在这种状态下系统的垃圾回收机制就准备回收该对象使用的内存。在回收内存时系统会调用可恢复对象的finalize()方法进行资源的清理。实务上finalize()方法既可以将可恢复对象变成可达态状态又可以将它变成不可达状态。
③不可达态:当一个对象完全失去引用而且finalize()方法也没有让它变成可达态,那么这个对象就进入了不可达状态。只有进入不可达状态的对象才会被系统正真的回收掉。
三种状态的转换图如下:
注意:垃圾回收只会回收堆内存中的对象而不会回收物理资源(如数据库链接,网络IO等资源)。
强制垃圾回收
首先声明垃圾回收只能由系统决定(在系统非常需要内存的时候)而且一般情况下系统什么时候回收垃圾是不确定的。我们的程序只能够控制某个对象不再有任何引用而无法左右垃圾什么时候回收。所以我们的强制回收机制也只是告诉系统要准备回收垃圾了,即使是这样垃圾回收也具有不确定性。但是在实务上我们运行了强制回收程序的时候系统还是会做出明显的反馈的。
一般的强制回收语句如下:
System.gc();
或Runtime.getRuntime().gc();
finalize()方法
finalize()方法是Java默认指定的垃圾回收方法。它定义在Object类中,所以我们可以重写它来做一些测试。同时finalize方法有如下的特点:
①永远不要自己去调用这个方法,它只能由垃圾回收机制去调用。
②垃圾回收机制的不确定性也导致了调用finalize方法的不确定性。
③在JVM调用finalize方法之后可恢复的对象可能变成垃圾被回收也可能变成可达状态。
④在调用finalize方法时如果出现异常那么也会继续执行。
以下代码演示了在调用finalize之后可恢复对象复活的过程:
package garbage;public class FinalizeTest {private static FinalizeTest ft = null; public void info() {System.out.println("测试资源清理的垃圾回收机制");}public static void main(String[] args) throws Exception{new FinalizeTest();System.gc();//这个确实通知系统执行了回收方法Thread.sleep(1000);//System.runFinalization();//它本身没有经过finalize方法,它只是在通知之后立即执行finalize方法ft.info();}public void finalize() {System.out.println("pass");ft = this;}
}
对象的几种引用
在Java程序中最常见的就是一个引用变量去引用该对象这是强引用这种状态下对象不可能被回收。除此之外在java.lang.ref包中还有其它的三种引用分别是软引用(SoftReference),弱引用(WeakReference),虚引用(PhantomReference不能单独使用必须配合引用队列)。这三种引用对象可以被垃圾回收器所回收。正是因为这样它们才能够跟踪对象是否被回收。
以下代码是弱引用被回收的过程
package garbage;import java.lang.ref.WeakReference;public class ReferenceTest {public static void main(String[] args) {String str = new String("zhuliu7.com");WeakReference<String> wr = new WeakReference<String>(str);//创建一个弱引用str = null;System.out.println(wr.get());//取出弱引用所引用的对象//强制回收System.gc();System.runFinalization();System.out.println(wr.get());//输出null}
}
注意:代码中需要注意的是在new一个字符串的时候我们不能直接去写一个字符串例如String s = “zhuliu7”;这样的话系统会使用常量池去管理这个字符串从而看不到上面的执行效果。
以下为上述程序中各个引用与对象的关系:
引用队列
当引用队列联合使用软引用,弱引用和虚引用的时候,系统在回收掉所引用的对象之后会将对应的引用添加到引用队列中。实务上我们可以通过检查引用队列中的值来了解垃圾回收的情况。以下代码是虚引用回收的一个例子:
package garbage;import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;public class PhantomReferenceTest {public static void main(String[] args) {String str = new String("zhuliu7.com");//不能直接使用字符串否则所引用的对象会由常量池来管理ReferenceQueue<String> rq = new ReferenceQueue<String>();PhantomReference pr = new PhantomReference(str,rq);System.out.println(pr.get());//输出null,虚引用就像没有引用一样System.out.println(rq.poll());//此时队列为空str = null;System.gc();System.runFinalization();System.out.println(rq.poll()==pr);//输出true说明对象已被回收}
}
总结:判断一个对象有没有被回收可以用弱引用或软引用对象的get();方法,或者是虚引用对象配合引用队列,队列中的元素来判断。
这些引用的作用是什么(也许这个话题应该放在前面去讨论的)
我们研究垃圾回收机制就是希望尽可能的减少程序在生命周期中所占用内存的大小,如果以软引用,弱引用,虚引用的方式去引用对象那么虚拟机就能够非常灵活的回收这些对象。这时候这些引用就会大有用途!
由于垃圾回收的不确定性,当我们希望能够从这些引用中获取对象时有可能这些对象就已经被回收。这时候我们需要重新创建一个对象,以下是代码演示:
package reference;import java.lang.ref.WeakReference;public class CreateReference {public static void main(String[] args) throws Exception {WeakReference<String> wr = new WeakReference<String>(new String("zhuliu7"));String str = null;//强制垃圾回收System.gc();System.runFinalization();System.out.println(str);str = wr.get();if(str==null) {System.out.println("pass");wr = new WeakReference<String>(new String("zhuliu7"));str = wr.get();System.out.println(str);str = null;//当赋值之后我们再次切断强引用}}
}
This is all : -)