程序远通过new关键字创建java对象,即视为java对象申请内存空间,Jvm会在对内存中为每个对象分配空间;当一个java对象失去引用时,JVM的垃圾回收机制会自动清除它们,并且回收它们所占的内存空间。
对象在内存中的状态:
将jvm内存中对象引用理解成一种有向图,把线程对象当成有向图的定点。顶点科大的对象垃圾回收机制不会回收它们;如果某个对象在这个邮箱途中处于不可达状态,认为这个对象不再被引用,接下来垃圾回收机制就会主动回收它了。
当一个对象在堆内存中运行时,根据它在对应有向图中的状态,可以把它所处的状态分为如下三种:
可达状态:当一个对象被创建之后,有一个以上的引用变量引用他。在有向图中可以从起始顶点导航该对象,那么它就处于可达状态,程序可通过引用变量来调用该对象的属性和方法。
可恢复状态:如果程序中某个对象不再有任何引用变量引用它,它将进入可恢复状态,此时从有向图的起始顶点不能导航到该对象。在这个状态下,系统的垃圾回收机制准备回收该对象所占用的额内存。在回收该对象之前,系统会调用可恢复状态的对象的finalize方法进行资源清理,如果系统在调用finalize方法重新让一个以上引用变量引用该对象,则这个对象再次变成可达状态,否则,该对象将会进入不可达状态。
不可达状态:当对象的所有关联都被切断,且系统调用所有对象的finalize方法依然没使该对象变成可达状态,这个对象将永久性失去引用,最后变成不可达状态。只有当一个对象处于不可达状态时,系统才会真正回收该对象所占用的资源。
对象的4中引用
对垃圾回收机制来说,判断一个对象是否可回收的标准就在于该对象是否被引用,因此引用也是JVM进行内存管理的一个重要概念。JDK 1.2开始,java在java.lang.ref包下提供了三个类:SoftReference、 PhantomReference和WeakReference。它们代表系统对对象的三种引用方式:软引用、虚引用和弱引用。
归纳起来,java语言对对象的引用有如下4种:
强引用、软引用、弱引用、虚引用
强引用:
java程序最长键的引用方式,程序创建一个对象,并把这个对象赋值给一个引用变量,这个引用变量就是强引用。
当对象处于强用用时,java对象处于可达状态,绝对不会被垃圾回收机制回收。因为JVM肯定不会回收强引用所引用的java对象,因此强引用是造成java内存泄露的主要原因之一。
软引用:
软引用通过SoftReference类来实现。一个对象只具有软引用时,它有可能被垃圾回收机制回收。对于软引用的对象而言,党法系统内存空间足够的时候,它就相当于强引用,不会被系统回收,系统也可以使用该对象。当系统内存空间不足时,系统将会回收它。
软引用是强引用很好的替代品,当程序需要大量创建某个类的新对象,而且有可能重新访问以创建老对象时可以充分使用软引用来解决内存紧张问题。
例如:需要访问1000个Person对象,可以有两种方式:
1、一次创建1000个Person对象,但只有一个Person引用指向最后一个Person对象
2、定义一个长度为1000的Person数组,每个数组元素引用一个Person对象
第一种情况而言,弱点很明显:程序不允许需要重新访问前面创建的Person,即使这个对象所占用的堆空间还没有被回收。但是已经失去了这个对象的引用,因此也不得不重新创建一个新的Person对象,已有的Person对象只能等待垃圾回收。
第二种情况,优势是可以随时重新访问前面创建的每个Person对象,弱点是如果对内存紧张,而1000个Person对象都被强引用引用者,垃圾回收机制也不可能回收它们的堆内存空间,会导致内存溢出。
使用软引用是一种很好的解决方案。
class Person{ String name; int age; public Person(String name , int age){ this.name = name; this.age = age; } public String toString(){ return "Person[name=]" + name + ", age=" + age + "]"; } } public class SoftReferenceTest { public static void main(String[] args){ SoftReference<Person>[] people = new SoftReference[100]; for(int i = 0; i < people.length; i++){ people[i] = new SoftReference<Person>(new Person("名字" + i ,(i + 1) * 4 % 100)); } System.out.println(people[2].get()); System.out.println(people[4].get()); //通知系统进行垃圾回收 System.gc(); System.runFinalization(); System.out.println(people[2].get()); System.out.println(people[4].get()); } }
弱引用:
弱引用与软引用有点相似,区别在于弱引用所引用对象的生存期更短,使用WeakReference类实现,引用级别更低。对于只弱引用的对象而言,系统垃圾回收机制运行时,无论系统内存是否够,总会回收哪些对象所占用的内存。
例子:
public class WeakReferenceTest { public static void main(String[] args){ String str = new String("疯狂java讲义"); WeakReference<String> wr = new WeakReference<String>(str); //切断str引用和“疯狂java讲义”之间的引用 str = null; System.out.println(wr.get()); System.gc(); System.runFinalization(); System.out.println(wr.get()); } }
弱引用具有很大的不确定性,因为每次垃圾回收机制执行时都会回收弱引用所引用的对象,而垃圾回收机制不受程序员的空值,所以程序获取弱引用所引用的java对象时必须小心空指针异常。
两种风格代码解决:
obj = wr.get(); if(obj == null){ wr = new WeakReference(recreateIt()); obj = wr.get(); } ...//操作obj对象 obj = null; ------------------------- obj = wr.get(); if(obj == null){ obj = recreateIt(); wr = new WeakReference(obj); } ... obj = null;
与WeakReference功能类似的还有WeakHashMap.其实程序很少考虑直接使用单个的WeakReference来用用某个对象,因此这种时候系统内存往往不会特别紧张.当程序有大量的java对象需要使用弱引用来引用时,可以考虑使用WeakHashMap来保存它们.
class CrazyKey{ String name; public CrazyKey(String name){ this.name = name; } public int hashCode(){ return name.hashCode(); } public boolean equals(Object obj){ if(obj == this){ return true; } if(obj != null && obj.getClass() == CrazyKey.class){ return name.equals(((CrazyKey)obj).name); } return false; } public String toString(){ return "CrazyKey[name=" + name + "]"; } } public class WeakHashMapTest { public static void main(String[] args) throws Exception{ WeakHashMap<CrazyKey , String> map = new WeakHashMap<CrazyKey , String>(); for(int i = 0; i < 10; i++){ map.put(new CrazyKey(i + 1 + "") , "value" + (i + 11)); } //垃圾回收之前,WeakHashMap与普通HashMap并无区别 System.out.println(map); System.out.println(map.get(new CrazyKey("2"))); System.gc(); Thread.sleep(50); System.out.println(map); System.out.println(map.get(new CrazyKey("2"))); } }
在垃圾回收机制运行之前,WeakHashMap与普通HashMap并无太大区别.但一旦垃圾回收机制被运行,WeakHashMap中所有的key-value对都会被清空,除非某些key还有强引用在引用它们.