对于长期运行的引用程序来说,如果无用的对象所占用的内存空间不能得到及时的释放的话,那么在一个局部的时间内便形成了事实上的内存泄露,如果要及时地释放内存,在Java 中最稳妥的方法就是—-在使用完对象之后立即执行“object=null”语句。当然,这也是一种理想状态。
在JDK中引入了4种对象引用类型,通过如下四种引用类型强行调用垃圾回收方法System.gc()来解决内存泄露问题。
- 强引用:在日常编程应用中所用的大多数引用类型都属于强引用类型,方法是显式执行“object=null”语句。
- 软引用:被软引用的对象,如果内存空间足够,垃圾回收器不会回收它的,如果内存空间不足,垃圾回收器将回收这些对象所占用的空间。在Java 中软引用相对应着java.lang.ref.SoftReference类。一个对象如果要被软引用,只需将其作为参数传入SoftReference 类的构造方法中就行了。
- 弱引用:与前面的软引用相比,被弱引用的对象拥有更短的内存时间(也就是生命周期)。垃圾回收器一旦发现了被弱引用的对象,不管当前内存空间是否不足,都会回收它的内存空间,若引用对应着java.lang.ref.WeakReference类。同样的道理,一个对象如果想被弱引用,只需要将其作为参数传入 WeakReference类的构造方法中就行了。
- 虚引用:虚引用不是真是可用的引用类型,完全可以视为一种“形同虚设”的引用类型。设计虚引用类型的目的在于结合引用关联队列,实现对象引用关系的跟踪。在 Java 中许引用对应着 java.lang.ref.PhantomReference 类。一个对象如果要被虚引用,只需将其作为参数传入 PhantomReference 类的构造方法中即可,同时作为参数传入的还用引用关系队列 java.lang.ref.ReferenceQueue 的对象实例。
public class Main {
public static void main(String[] args) {
int length = 10;
//创建强引用
Set<MyObject> a = new HashSet<>();
for(int i = 0; i<length; i++)
{
MyObject ref = new MyObject("Hard_" + i);
System.out.println("创建强引用:" + ref);
a.add(ref);
}
a=null;
System.gc();
//创建软引用
Set<SoftReference<MyObject>> sa = new HashSet<>();
for(int i = 0; i<length; i++)
{
SoftReference<MyObject> ref = new SoftReference<MyObject>(new MyObject("Soft_" + i));
System.out.println("创建软引用:" + ref.get());
sa.add(ref);
}
sa=null;
System.gc();
//创建弱引用
Set<WeakReference<MyObject>> wa = new HashSet<>();
for(int i = 0; i<length; i++)
{
WeakReference<MyObject> ref = new WeakReference<MyObject>(new MyObject("Weak_" + i));
System.out.println("创建弱引用:" + ref.get());
wa.add(ref);
}
System.gc();
//创建需引用
ReferenceQueue<Object> rq = new ReferenceQueue<>();
Set<PhantomReference<MyObject>> pa = new HashSet<>();
for(int i=0; i<length; i++)
{
PhantomReference<MyObject> ref = new PhantomReference<MyObject>(new MyObject("Phantom_" + i), rq);
System.out.println("创建虚引用:" + ref.get());
pa.add(ref);
}
System.gc();
}
}
class MyObject
{
private String id;
public MyObject(String id)
{
this.id=id;
}
public String toString()
{
return id;
}
public void finalize()
{
System.out.println("回收对象:"+ id);
}
}
输出:
创建强引用:Hard_0
创建强引用:Hard_1
创建强引用:Hard_2
创建强引用:Hard_3
创建强引用:Hard_4
创建强引用:Hard_5
创建强引用:Hard_6
创建强引用:Hard_7
创建强引用:Hard_8
创建强引用:Hard_9
回收对象:Hard_3
创建软引用:Soft_0
回收对象:Hard_1
创建软引用:Soft_1
回收对象:Hard_0
创建软引用:Soft_2
回收对象:Hard_9
创建软引用:Soft_3
回收对象:Hard_8
创建软引用:Soft_4
回收对象:Hard_7
创建软引用:Soft_5
回收对象:Hard_6
创建软引用:Soft_6
回收对象:Hard_5
创建软引用:Soft_7
回收对象:Hard_4
回收对象:Hard_2
创建软引用:Soft_8
创建软引用:Soft_9
回收对象:Soft_4
回收对象:Soft_2
创建弱引用:Weak_0
回收对象:Soft_1
创建弱引用:Weak_1
回收对象:Soft_0
回收对象:Soft_5
回收对象:Soft_3
回收对象:Soft_9
回收对象:Soft_8
回收对象:Soft_7
回收对象:Soft_6
创建弱引用:Weak_2
创建弱引用:Weak_3
创建弱引用:Weak_4
创建弱引用:Weak_5
创建弱引用:Weak_6
创建弱引用:Weak_7
创建弱引用:Weak_8
创建弱引用:Weak_9
回收对象:Weak_7
回收对象:Weak_2
回收对象:Weak_1
回收对象:Weak_0
回收对象:Weak_5
回收对象:Weak_4
回收对象:Weak_3
回收对象:Weak_9
回收对象:Weak_8
回收对象:Weak_6
创建虚引用:null
创建虚引用:null
创建虚引用:null
创建虚引用:null
创建虚引用:null
创建虚引用:null
创建虚引用:null
创建虚引用:null
创建虚引用:null
创建虚引用:null
回收对象:Phantom_8
回收对象:Phantom_2
回收对象:Phantom_1
回收对象:Phantom_0
回收对象:Phantom_5
回收对象:Phantom_4
回收对象:Phantom_3
回收对象:Phantom_9
回收对象:Phantom_7
回收对象:Phantom_6
其中,MyObject 中的finalize()方法是告诉垃圾回收器应该执行的操作,该方法从Object类继承而来。在从堆中永久删除对象之前,垃圾回收器调用该对象的Finalize方法。
可见,当a=null时。gc便回收其所占用内存。sa也同理。而wa的话,gc发现立刻回收其内存。pa只是对对象引用关系的跟踪而已,并没有真正的实例存在。