java虚拟机之引用和可触及性的强度

       在java中提供了四个级别的引用:强引用,软引用,弱引用和虚引用。
       强引用:只要引用存在,垃圾回收器永远不会回收Object obj = new Object();
//可直接通过obj取得对应的对象 如obj.equels(new Object());
而这样 obj对象对后面new Object的一个强引用,只有当obj这个引用被释放之后,对象才会被释放掉,这也是我们经常所用到的编码形式。这里不再解释。
       软引用:软引用是比强引用弱一点的引用。一个对象只持有软引用,那么当堆空间不足时,就会被回收。下面展示软引用在内存不足时被回收的例子

package gcExample;

import java.lang.ref.SoftReference;

public class SoftRef {
    public static class User {
        public int id;
        public String name;

        public User(int id, String name) {
            this.id = id;
            this.name = name;
        }
        @Override
        public String toString() {
            return "[id=" + String.valueOf(id) + ",name=" + name + "]";
        }
    }

    public static void main(String[] args) {
        User u = new User(1, "geym");
        SoftReference<User> userSoftRef = new SoftReference<SoftRef.User>(u);
        u = null;

        System.out.println(userSoftRef.get());
        System.gc();
        System.out.println("After GC");
        System.out.println(userSoftRef.get());

        byte[] b = new byte[1024 *925 * 7];
        System.gc();
        System.out.println(userSoftRef.get());
    }
}

运行上述代码得到:

[id=1,name=geym]
After GC
[id=1,name=geym]
null

因此得到结论:gc未必会回收软引用的对象,但是,当内存资源紧张时,软引用对象会被回收,所以软引用对象不会引起内存溢出。
每一个软引用都可以附带一个引用队列,当对象的可达性状态发生改变时(由可达变为不可达),软引用对象就会进入引用队列。通过这个队列,可以跟踪对象的回收情况,请看下面代码

package gcExample;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;

public class SoftRefQ {
    public static class User {
        public int id;
        public String name;

        public User(int id, String name) {
            this.id = id;
            this.name = name;
        }
        @Override
        public String toString() {
            return "[id=" + String.valueOf(id) + ",name=" + name + "]";
        }
    }
    static ReferenceQueue<User> softQueue=null;
    public static class CheckRefQueue extends Thread{
        @Override
        public void run(){
            while(true){
                if(softQueue!=null){
                    UserSoftReference obj=null;
                    try{
                        obj =(UserSoftReference) softQueue.remove();
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    if(obj!=null){
                        System.out.println("user id "+ obj.uid +"is delete");
                    }
                }
            }
        }
    }
    public static class UserSoftReference extends SoftReference<User>{
        int uid;
        public UserSoftReference(User referent,ReferenceQueue<? super User> q){
            super (referent,q);
            uid=referent.id;
        }
    }
    public static void main(String[] args) throws InterruptedException{
        Thread t= new CheckRefQueue();
        t.setDaemon(true);
        User u=new User(1,"geym");
        softQueue= new ReferenceQueue<SoftRefQ.User>();
        UserSoftReference userSoftRef = new UserSoftReference(u,softQueue);
        u=null;
        System.out.println(userSoftRef.get());
        System.gc();
        //內存足夠
        System.out.println("After gc");
        System.out.println(userSoftRef.get());

        System.out.println(" try to create byte array and gc ");
        byte [] b= new byte [1024* 925 *7];
        System.gc();
        System.out.println(userSoftRef.get());

        Thread.sleep(1000);
    }
}

       上述代码extends SoftReference 实现了一个自定义的软引用类,扩展软引用的目的是记录User.uid,后续再引用队列中,就可以通过这个uid字段知道哪个User实例被回收了。
       值得注意的是,在创建软引用时(UserSoftReference userSoftRef = new UserSoftReference(u,softQueue),制定了一个软引用队列,当给定的对象实例被回收时,就会被加入这个引用队列,通过访问该队列可以跟踪对象的回收情况。
上述代码执行结果:

[id=1,name=geym]
After gc
[id=1,name=geym]
try to create byte array and gc 
user id 1 is delete
null

弱引用:
       弱引用是一种比软引用较弱的引用类型,在系统gc时,只要发现弱引用,不管系统堆空间使用情况怎么样,都会将对象进行回收。但是由于垃圾回收器的线程通常优先级很低,因此,斌那个不一定能很快的发现持有弱引用的对象。在这种情况下,弱引用对象可以存较长的时间。一旦一个弱引用对象被垃圾回收器回收,便会加入到一个注册的引用队列中(和软引用很像)下面给出例子

package gcExample;

import java.lang.ref.WeakReference;

public class WeakRef {
    public static class User {
        public int id;
        public String name;

        public User(int id, String name) {
            this.id = id;
            this.name = name;
        }
        @Override
        public String toString() {
            return "[id=" + String.valueOf(id) + ",name=" + name + "]";
        }
    }

    public static void main(String[] args) {
        User u = new User(1, "geym");
        WeakReference<User> userWeakRef = new WeakReference<User>(u);
        u = null;
        System.out.println(userWeakRef);
        System.gc();
        // 不管当前内存空间是否够用 ,都会回收它的内存
        System.out.println("after gc");
        System.out.println(userWeakRef.get());
    }
}

执行结果:

java.lang.ref.WeakReference@2a139a55
after gc
null

弱引用和软引用一样,在构造弱引用时,也可以指定一个引用队列,当弱引用对象被回收时,就会加入指定的引用队列,通过这个队列可以跟踪对象的回收情况
       注意:软引用、弱引用都非常适合来保存那些可有可无的缓存数据。如果这么做,当系统内存不足时,这些缓存数据会被回收。不会导致内存溢出。而当内存资源充足时,这些缓存数据又可以存在相当长的时间,从而起到加速系统的作用。
虚引用:
       虚引用是所有引用类型中最弱的一个。一个持有虚引用的对象,和没有引用几乎是一样的,随时都可能被垃圾回收器回收。当试图通过虚引用get()方法取得强引用时,总是会失败。并且,虚引用必须和引用队列一起使用,作用在于跟踪垃圾回收过程。
       当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象后,将这个虚引用加入引用队列,以通知应用程序对象的回收情况。
       下面给出实例,使用虚引用跟踪一个可复活对象的回收

package gcExample;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class TraceCanReliveObj {
    public static TraceCanReliveObj obj;
    static ReferenceQueue<TraceCanReliveObj> phantomQueue = null;

    public static class CheckRefQueue extends Thread {
        @Override
        public void run() {
            while (true) {
                if (phantomQueue != null) {
                    PhantomReference<TraceCanReliveObj> objt = null;
                    try {
                        objt = (PhantomReference<TraceCanReliveObj>) phantomQueue
                                .remove();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (objt != null) {
                        System.out.println("TraceCanReliveObj is delete by gc");
                    }
                }
            }
        }
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("CanReliveObj finalize called ");
        obj = this;
    }

    @Override
    public String toString() {
        return " i am CanReliveObj";
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t = new CheckRefQueue();
        t.setDaemon(true);
        t.start();
        phantomQueue = new ReferenceQueue<TraceCanReliveObj>();
        obj = new TraceCanReliveObj();
        PhantomReference<TraceCanReliveObj> phantomRef = new PhantomReference<TraceCanReliveObj>(
                obj, phantomQueue);
        obj = null;
        System.gc();
        Thread.sleep(1000);
        if (obj == null) {
            System.out.println(" obj is null");
        } else {
            System.out.println(" obj 可用");
        }
        System.out.println("第二次gc");
        obj = null;
        System.gc();
        Thread.sleep(1000);
        if (obj == null) {
            System.out.println(" obj is null");
        } else {
            System.out.println(" obj 可用");
        }
    }
}

       执行上述代码得到如下输出:

CanReliveObj finalize called 
 obj 可用
第二次gc
TraceCanReliveObj is delete by gc
 obj is null

       由于虚引用可以跟踪对象的回收时间,因此,也可以将一些资源释放操作放置在虚引用中执行和记录。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值