颜群JVM【05】强软弱虚引用

颜群JVM【05】强软弱虚引用

JVM四种引用级别

如果一个对象存在着指向它的引用,那么这个对象就不会被GC回收? 答案:不对,有局限性,这个说法只适用于强引用。

根据引用的强弱关系: 强引用>软引用>弱引用>虚引用

各种引用的出处:

  • 强引用: new

  • 软引用 弱引用 虚引用 最终引用:Reference

强引用

Object obj = new Object() ;

约定: 引用: obj,引用对象: new Object()

强引用对象什么失效?

1.生命周期结束(作用域失效)

java 复制代码
public void method(){
  Object obj = new Object() ;
} //当方法执行完毕后,强引用指向的引用而对象 new Object() 就会等待被GC回收

2.引用被置为null,引用对象被GC回收

java 复制代码
obj = null ;//此时,没有任何引用指向new Object() 因此,new Object() 就会等待被GC回收

除了以上两个情况以外,其他任何时候GC都不会回收强引用对象。

软引用

根据JVM内存情况: 如果内存充足,GC不会随便的回收软引用对象;如果JVM内存不足,则GC就会主动的回收软引用对象。

软引用:java.lang.ref.SoftReference

Reference中有一个get()方法,用于返回所引用的对象

复制代码
SoftReference<SoftObject> softRef = new SoftReference<>(new SoftObject());

softRef.get() -->返回引用所指向的SoftObject对象本身

java 复制代码
//软引用对象
class SoftObject {
}

public class SoftReferenceDemo {
    public static void main(String[] args) throws Exception {
        //softRef  -->  SoftObject  设计模式中的:装饰模式
        SoftReference<SoftObject> softRef = new SoftReference<>(new SoftObject());

        //开启一个线程,监听,软引用是否已经被回收
        new Thread(() -> {
            while (true) {
                System.out.println();  // 加上该句话,while(true)和判断语句就有了空隙,就有时间与主内存交互softRef了
                if (softRef.get() == null) { //在while(true)里边,线程判断的太快,没时间从主内存刷新内容过来
                    System.out.println("软引用对象已被回收..");
                    System.exit(0);
                }
            }
        }, "自定义线程").start();

        //不断的往集合中 存放数据,模拟内存不足
        List<byte[]> list = new ArrayList<>();
        while (true) {
            if (softRef.get() != null) list.add(new byte[1024 * 1024 * 1]);//每次向list中增加1m内容
//            Thread.sleep(10);
        }
    }
}

弱引用

回收的时机:只要GC执行,就会将弱引用对象进行回收。

java.lang.ref.WeakReference<T>

java 复制代码
package ref;

import java.lang.ref.WeakReference;

public class WeakReferenceDemo {
    public static void main(String[] args) throws Exception {

        WeakReference<Object> weakRef = new WeakReference<>(new Object());
        //weakRef->Object

        System.out.println( weakRef.get()==null   ? "已被回收":"没被回收"  );

        System.gc();//建议GC执行一次回收(存在概率)
        
        Thread.sleep(100);

        System.out.println( weakRef.get()==null   ? "已被回收":"没被回收"  );

    }
}

虚引用(幻影引用或者幽灵引用)

java.lang.ref.PhantomReference<T>

是否使用虚引用,和引用对象本身没有任何关系;无法通过虚引用来获取对象本身

一般:引用get() -> 引用对象;但是:虚引用get() -> null

因此虚引用不会单独使用,必须和引用队列(java.lang.ref.ReferenceQueue)一起使用。

价值: 当gc回收一个对象,如果gc发现此对象还有一个虚引用,就会将虚引用放入到引用队列中,之后(当虚引用出队之后)再去回收该对象。因此,我们可以使用虚引用+引用队列实现:在对象被gc回收之前,进行一些额外的其他操作。

GC->如果有虚引用->虚引用入队->虚引用出队->回收对象

java 复制代码
class MyObject {
}

public class PhantomReferenceDemo {
    public static void main(String[] args) throws Exception {
        MyObject obj = new MyObject();
        ReferenceQueue<MyObject> queue = new ReferenceQueue<>();

        //虚引用+引用队列
        PhantomReference<MyObject> phantomRef = new PhantomReference<>(obj, queue);

        //让gc执行一次回收操作
        obj = null;
        System.gc();
        Thread.sleep(30);
        System.out.println("GC执行...");

        //GC-> 虚引用 -> 入队 -> 出队 -> obj
        System.out.println(queue.poll());
    }
}

特殊情况:如果虚引用对象重写了finalize(),那么JVM会延迟虚引用的入队时间。

java 复制代码
class MyObject3 {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize...");
    }
}

public class PhantomReferenceDemo2 {
    public static void main(String[] args) throws Exception {
        MyObject3 obj = new MyObject3();
        ReferenceQueue queue = new ReferenceQueue();
        PhantomReference<MyObject3> phantomRef = new PhantomReference<>(obj, queue);

        //让gc执行一次回收操作
        obj = null;
        System.gc();
        Thread.sleep(30);
        System.out.println("GC执行...1");

        //GC-> 虚引用->入队->出队->     obj
        System.out.println(queue.poll());//虚引用并没有入队

        System.gc();
        Thread.sleep(30);
        System.out.println("GC执行...2");

        //GC-> 虚引用->入队->出队->     obj
        System.out.println(queue.poll());//虚引用延迟到了第二次gc时入队
    }
}

最终引用

final class Finalizer extends FinalReference

C/C++中,对象产生:构造方法() ,对象释放:析构函数()

在Java中存在 Finalizer 可以帮我们自动的回收一些不需要的对象,因此不需要写析构函数。

jvm能够直接操作的是:非直接内存

直接内存:native (操作系统中的内存,而不是jvm内存)

jvm 不能操作直接内存(非jvm操作的内容)时,而恰好此区域的内容又忘了关闭,此时Finalizer就会将这些内存进行回收。

使用软引用实现缓存的淘汰策略

java ->缓存( 90% -> 60%【淘汰30%】) -> db(iphone)

一般的淘汰策略:根据容量/缓存个数 + LRU 进行淘汰。

在 Java中 还可以用引用实现淘汰策略。

MyObject obj = new MyObject();

map.put( id , obj ) ; //强引用,不会被GC回收

map.put(id, 软引用(obj) );//当jvm内存不足时,会主动回收。

java 复制代码
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;

class MyObject10 {
}

public class SoftDemo {
    //map: key:id  ,  value:对象的软引用  (拿对象:对象的软引用.get() )
    Map<String, SoftReference<MyObject10>> caches = new HashMap<>();
    //java -> caches -> db
    //set: db->caches
    //get: java->cache

    void setCaches(String id, MyObject10 obj) {
        caches.put(id, new SoftReference<MyObject10>(obj)); //当jvm内存不足时,会主动回收。
    }

    MyObject10 getCache(String id) {
        SoftReference<MyObject10> softRef = caches.get(id);
        return softRef == null ? null : softRef.get();
    }

    //优势:当jvm内存不足时,gc会自动回收软引用。因此本程序无需考虑OOM问题。
}
相关推荐
勤奋菲菲11 小时前
使用Mybatis-Plus,以及sqlite的使用
jvm·sqlite·mybatis
稚辉君.MCA_P8_Java13 小时前
JVM第二课:一文讲透运行时数据区
jvm·数据库·后端·容器
杨DaB1 天前
【JavaSE】JVM
java·jvm
code小毛孩1 天前
如何简单的并且又能大幅度降低任务队列的锁粒度、提高吞吐量?
java·jvm·数据库
用手手打人1 天前
JVM(九)-- 类的生命周期
jvm
深海呐1 天前
Android 编译速度优化:JVM堆内存扩充
android·jvm·jvm内存扩充·android 加快编译速度
哈基米喜欢哈哈哈1 天前
低版本的JVM遇到高版本的class字节码是否会报错
java·jvm
用手手打人1 天前
JVM(八)-- Class文件
jvm
235161 天前
【并发编程】详解volatile
java·开发语言·jvm·分布式·后端·并发编程·原理