java引用类型

本文详细介绍了Java中的四种引用类型:强引用、软引用、弱引用和幽灵引用,并通过实例展示了每种引用类型的特性和应用场景。

java中有四种引用类型,或者说是引用方式。

  • 强引用(String Reference):即时进行了多次的GC回收,即使JVM的内存真的已经不够用了,即使JVM最终不得已抛出了OOM错误,那么该引用继续抢占;
  • 软引用(Soft Reference):当我们内存空间不足时,可以回收此内存空间。如果内存空间充足,则不回收。可以用其完成一些缓存的处理操作。
  • 弱引用(Weak Reference):不管内存是否紧张,只要一出现GC处理,则立即回收。
  • 幽灵引用(Phantom Reference):和没有引用是一样的。

 

 

强引用

强引用是java默认支持的一种操作模式,也就是说,在我们引用的处理期间,即使出现有GC,即使内存不足,该引用的数据也不会被回收。

Object oa = new Object(); Object ob = oa; oa = null; //oa对象已经断开了对原始的引用 System.gc(); //进行垃圾回收 System.out.println(ob);

ob对象被打印出来,不是null。

强引用中只可能所有对象断开连接后,才可以成为垃圾空间,才有可能被回收。而即使有一个对象引用此空间,那么该对象也不会被回收。

-Xmx10m -Xms10m -XX:+PrintGCDetails
Object oa = new Object();
Object ob = oa;
oa = null;   //oa对象已经断开了对原始的引用
System.gc();  //进行垃圾回收
System.out.println(ob);
while (true){
     new Thread(() -> {//lamada表达式
        String str = "lihao";
        for (int i = 0; i < 1000; i++) {//制造一个错误,产生垃圾。
           str += str + i;
           str.intern();
        }
        Object oa = new Object();
        Object ob = oa;
        oa = null;   //oa对象已经断开了对原始的引用
        System.gc();  //进行垃圾回收
//          System.out.println(ob);
        try {
           Thread.sleep(100);
        } catch (InterruptedException e) {
           e.printStackTrace();
        }
     }).start();
  }

即使产生了GC,也不会释放强引用的空间。

强引用并不是造成OOM的关键因素,正常来讲,你没一个用户(线程)操作完成后,该对象都可以被很容易的被回收。

 

 

软引用

当我们内存不足的时候,才进行GC的空间释放,但是如果想使用软引用,必须使用单独特殊的处理类:java.lang.ref.SoftReference,该类的定义如下:

    • 构造方法: public SoftReference(T reference)
    • 取得引用的数据:public T get()
String str = "lihao";
SoftReference<String> ref = new SoftReference<>(str);
str = null;   //强引用断开连接
System.gc();   //因为此时的空间还很富裕,所以软引用不会释放
System.out.println(ref);

与强引用相比,最大的特点在于:软引用中保存的内容如果在内存富裕的时候会继续保留,内存不足时,会作为第一批的丢弃者进行垃圾空间的释放。

在开发中可以利用软引用实现高速缓存组件。

 

弱引用

弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

WeakReference<String> sr = new WeakReference<String>(new String("lihao"));
System.out.println(sr.get());    //lihao
System.gc();                //通知JVM的gc进行垃圾回收
System.out.println(sr.get());    //null

 

虚引用(幽灵引用)

虚引用顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的声明周期。如果一个对象仅持有虚引用,那么他就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

ReferenceQueue<String> queue = new ReferenceQueue<String>();
PhantomReference<String> pr = new PhantomReference<String>(new String("lihao"), queue);
System.out.println(pr.get());

程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现程序某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

 

 

引用队列ReferenceQueue

就是一个引用队列,如果保存的是Reference对象本身,如果:Reference引用指向的对象被GC回收,其实Reference已经无效了。

这种Reference将被放入引用队列,可以在这里将其清除,避免占有空间。

final ReferenceQueue queue = new ReferenceQueue();
String str = new String("lihao");
WeakReference wr = new WeakReference(str, queue);

Thread thread = new Thread() {
    @Override
    public void run() {
        try {
            Reference reference = queue.remove();
            System.out.println(reference + " event fired.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
};

thread.setDaemon(true);//标记为守护线程
thread.start();
System.out.println("Reference Queue is listening");

str = null;  //断开强引用
System.out.println("wr.get:"+wr.get());

System.out.println("Ready to GC");
System.gc();

try {
    Thread.sleep(2000);
}catch (Exception e){
    e.printStackTrace();
}

System.out.println("wr.get:"+wr.get());

 

WeakHashMap

弱引用map:就是Key键是一个弱引用的键,如果key键被回收,则在get该map中值后,会自动remove掉value。

如果key键始终被强引用,则无法被回收。

注意value是被强引用的,所以不要让value间接的引用了key键,这将导致key始终被强引用。

这个map 适合受key的生命周期控制的缓存。

String test1 = new String("test1");
String test2 = new String("test2");

HashMap<String, String> hashMap = new HashMap<>();
hashMap.put(test1, "test1");
hashMap.put(test2, "test2");

WeakHashMap<String, String> weakMap = new WeakHashMap<>();
weakMap.put(test1, "test1");
weakMap.put(test2, "test2");

weakMap.remove(test1);

//断开强引用
test1 = null;
test2 = null;

System.gc();
for (Map.Entry<String, String> entry : hashMap.entrySet()) {
    System.out.println(entry.getKey() + entry.getValue());
}
System.out.println("===============");
for (Map.Entry<String, String> entry : weakMap.entrySet()) {
    System.out.println(entry.getKey() + entry.getValue());
}

 

如何应用软引用避免OOM

假如有一个应用需要读取大量的本地图片,如果每次读取图片都从硬盘读取,则会严重影响性能,但是如果全部加载到内存当中,又有可能造成内存溢出,此时使用软引用可以解决这个问题。

private Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String,SoftReference<Bitmap>>();
public void addBitmapToCache(String path) {

        // 强引用的Bitmap对象

        Bitmap bitmap = BitmapFactory.decodeFile(path);

        // 软引用的Bitmap对象

        SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap);

        // 添加该对象到Map中使其缓存

        imageCache.put(path, softBitmap);

        }

public Bitmap getBitmapByPath(String path) {

        // 从缓存中取软引用的Bitmap对象

        SoftReference<Bitmap> softBitmap = imageCache.get(path);

        // 判断是否存在软引用

        if (softBitmap == null) {

        return null;

        }

        // 取出Bitmap对象,如果由于内存不足Bitmap被回收,将取得空

        Bitmap bitmap = softBitmap.get();

        return bitmap;

}

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值