Java引用类型与ThreadLocal

Java引用类型

强引用

csharp 复制代码
public class M {

    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalize");
    }
}

Object的finalize()方法是对象被回收时被调用的方法。一般来说不要去修改这个方法,执行你的代码可能会导致垃圾回收变慢,进而引发OOM的风险。

java 复制代码
public class NormalReference {
    public static void main(String[] args) throws IOException {
        M m = new M();
        m = null;
        System.gc(); //DisableExplicitGC
        System.out.println(m);

        System.in.read(); // 阻塞main线程,给垃圾回收线程时间执行
    }
}

输出结果:说明gc会在何时的时间回收没有引用的对象

csharp 复制代码
null
finalize

软引用

软引用适合用来做缓存

csharp 复制代码
public class T02_SoftReference {
    // 要做这个实验,需要把设置 -Xmx=20M 在idea中可以设置VM options = -Xmx20M
    public static void main(String[] args) {
        SoftReference<byte[]> sr = new SoftReference<>(new byte[1024 * 1024 * 10]);// 1024 * 1024 = 1M

        
        System.out.println(sr.get()); // 拿得到,没有回收
        System.gc();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(sr.get()); // 拿得到,没有回收

        // 再分配一个数组,heap将装不下,这时候系统会垃圾回收,先回收一次,如果不够,会把软引用干掉
        byte[] b = new byte[1024 * 1024 * 12];
        System.out.println(sr.get()); // 拿不到,回收了

    }
}

虚引用

虚引用的应用场景:

虚引用的数据永远拿不到,只有在回收的时候,把对象放进ReferenceQueue中。

csharp 复制代码
public class T04_PhantomReference {
    private static final List<Object> LIST = new LinkedList<>();
    private static final ReferenceQueue<M> QUEUE = new ReferenceQueue<>();

    public static void main(String[] args) throws InterruptedException {
        PhantomReference<M> phantomReference = new PhantomReference<>(new M(), QUEUE);
        System.out.println(phantomReference.get()); // 虚引用的数据永远拿不到

        ByteBuffer b = ByteBuffer.allocateDirect(1024);

        new Thread(() -> {
            while (true) {
                LIST.add(new byte[1024 * 1024]);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(phantomReference.get());
            }
        }).start();

        new Thread(() -> {
            while (true) {
                Reference<? extends M> poll = QUEUE.poll();
                if (poll != null) {
                    System.out.println("--- 虚引用对象被jvm回收了 ----" + poll.get());
                }
            }
        }).start();

        Thread.sleep(1);

    }
}

直接内存的使用方式:(这里面就包含了虚引用的使用)

arduino 复制代码
public class TestDirectByteBuffer {
    ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
}

弱引用

csharp 复制代码
public class T03_WeakReference {
   public static void main(String[] args) throws InterruptedException {
       WeakReference<M> wr = new WeakReference<>(new M());

       System.out.println(wr.get()); // 能拿到
       System.gc();
       Thread.sleep(1); // 睡1s让gc回收完
       System.out.println(wr.get()); // 拿不到,我们并没有把wr=null,但是System.gc()方法执行后直接就回收了,垃圾回收器看到弱引用就直接当垃圾回收了
   }
}

输出结果:

kotlin 复制代码
com.example.code.gc.M@4d7e1886
finalize
null

垃圾回收器看到弱引用就当垃圾看直接回收。

Q:那有这个跟没有这个引用有什么区别?不都是直接被gc回收吗?

A:经典应用场景就是ThreadLocal。WeakHashMap。

csharp 复制代码
public class ThreadLocal2 {
   static ThreadLocal<Person> tl = new ThreadLocal<>();

   public static void main(String[] args) {
       new Thread(() -> {
           try {
               Thread.sleep(1);
               // 第一个线程设置对象
               tl.set(new Person("zhangsan"));
               System.out.println(tl.get());
//                tl.remove();
           } catch (InterruptedException e) {
               throw new RuntimeException(e);
           }
       }).start();

       new Thread(() -> {
           try {
               Thread.sleep(2);
               // 第二个线程获取对象,拿不到,即使都是操作的同一个对象tl
               System.out.println(tl.get());
           } catch (InterruptedException e) {
               throw new RuntimeException(e);
           }
       }).start();
   }

   static class Person{
       public Person(String name) {
           this.name = name;
       }

       String name = "zhangsan";
   }
}

输出结果:同一个tl对象,都执行get方法,一个能拿到,一个拿不到

kotlin 复制代码
com.example.code.gc.ThreadLocal2$Person@1b9c704e
null

ThreadLocal

跟线程挂钩

拿到的是当前线程池的成员变量

Thread销毁的时候,ThreadLocalMap也被回收了,但是很多Thread是不会被回收的,比如说线程池里面的核心线程。要避免内存泄漏,这里entry的key设置为虚引用,外面的tl被设置为null就能保证key会被回收了。但是还是会有内存泄漏的风险。key为null了,value就访问不到。value是一个强引用。这种情况最好还是要调用tl.remove()方法进行回收。才能保证内存不会泄漏。

相关推荐
无声旅者2 小时前
深度解析 IDEA 集成 Continue 插件:提升开发效率的全流程指南
java·ide·ai·intellij-idea·ai编程·continue·openapi
Ryan-Joee3 小时前
Spring Boot三层架构设计模式
java·spring boot
Hygge-star3 小时前
【数据结构】二分查找5.12
java·数据结构·程序人生·算法·学习方法
dkmilk3 小时前
Tomcat发布websocket
java·websocket·tomcat
工一木子3 小时前
【Java项目脚手架系列】第七篇:Spring Boot + Redis项目脚手架
java·spring boot·redis
哞哞不熬夜3 小时前
JavaEE--初识网络
java·网络·java-ee
等等5434 小时前
Java EE初阶——wait 和 notify
java·开发语言
API小爬虫4 小时前
淘宝按图搜索商品(拍立淘)Java 爬虫实战指南
java·爬虫·图搜索算法
lyrhhhhhhhh4 小时前
Spring 框架 JDBC 模板技术详解
java·数据库·spring
亚林瓜子5 小时前
AWS Elastic Beanstalk控制台部署Spring极简工程
java·spring·云计算·aws·eb