• Java中的四种引用详解


    Java中的四种引用

    Java中有四种引用类型:强引用、软引用、弱引用、虚引用。

    强引用

    强引用是最常见的一种引用类型,在实际开发中,几乎都是强引用类型。

    Object obj = new Object();

    当我们的对象有强引用的时候,即使内存不足,JVM宁愿抛出OOM,也不会把该对象回收掉。如果需要让垃圾回收器回收此对象,需要将强引用关系打破,最简单的方式就是将对象赋值为null。下面写个例子做个简单说明:

    创建一个Order对象,为了更好的知晓垃圾回收的情况,这里重写 finalize() 方法,因为对象被垃圾回收时都会调用此方法。

    1. /**
    2. * 订单对象
    3. */
    4. public class Order {
    5. @Override
    6. protected void finalize() throws Throwable {
    7. System.out.println("order对象被垃圾回收");
    8. }
    9. }
    当对象的强引用关系被打破时,显式调用垃圾回收器
    1. /**
    2. * 强引用类型垃圾回收
    3. */
    4. public class NormalReferenceTest {
    5. public static void main(String[] args) throws IOException {
    6. Order order = new Order();
    7. // 破坏引用关系
    8. order = null;
    9. // 调用垃圾回收器,进行垃圾回收
    10. System.gc();
    11. System.out.println(order);
    12. // 阻塞Main主线程
    13. System.in.read();
    14. }
    15. }
    输出结果:
    1. null
    2. order对象被垃圾回收

    根据输出结果,可知Order对象被垃圾回收掉了。

    注意:

    在生产环境上,一定不要去重写 finalize() 方法,有内存溢出的风险。由于每个对象在进行垃圾回收时都会去调用该对象的 finalize() 方法,该方法默认是空的,啥都没做,执行效率是非常快的。但是如果重写后就会影响垃圾回收的效率,假设此时业务上还有大量的对象产生,垃圾回收的效率小于对象产生的效率,时间一长就会内存溢出。

    软引用

    软引用就是用 SoftReference 将引用对象包装一下,通过 get() 方法获取包装的对象,使用方式如下:

    1. SoftReference softReference = new SoftReference<>(new byte[1024 * 1024 * 10]);
    2. softReference.get();//获取软引用对象

    这里用一个例子说明软引用的特点

    1. -Xmx20M
    2. SoftReference
    3. get()
    4. get()
    5. get()

    代码如下:

    1. public class SoftReferenceTest {
    2. public static void main(String[] args) {
    3. SoftReference<byte[]> softReference = new SoftReference<>(new byte[1024 * 1024 * 10]);// 10M
    4. // 获取软引用对象
    5. System.out.println(softReference.get());
    6. // 调用垃圾回收
    7. System.gc();
    8. // 睡眠一下,给与垃圾回收时间
    9. try {
    10. Thread.sleep(200);
    11. } catch (InterruptedException e) {
    12. e.printStackTrace();
    13. }
    14. // 再次获取软引用对象
    15. System.out.println(softReference.get());
    16. // 再new一个字节数组,堆内存不够,会触发垃圾回收,如果内存空间还不够,就会把软引用引用的对象回收掉
    17. byte[] bytes = new byte[1024 * 1024 * 12];// 12M
    18. // 再次获取软引用对象,此时将会获取为null
    19. System.out.println(softReference.get());
    20. }
    21. }

    运行结果:

    1. [B@677327b6
    2. [B@677327b6
    3. null

    说明:

    第一次获取软引用对象是有值的,接着调用GC,GC垃圾回收后,第二次获取也是有值的,说明垃圾回收时先不会回收软引用对象。随后,继续往内存里面new一个12M的byte数组,显然,我们堆内存是20M,最开始的软引用对象占10M,这里在new 一个,内存是装不下的,所以触发了垃圾回收器回收10M的软引用对象,第三次获取软引用对象时为null,因为都已被垃圾回收器回收掉了。

    软引用的特点:当JVM进行垃圾回收时,如果第一次回收后内存足够,那么不会回收软引用对象;如果第一次回收后内存依旧不够,那么就会回收掉软引用对象。根据这个特点,软引用可以用来做缓存,当系统内存足够时,通过缓存获取值,如果内存不够时,先回收掉缓存,释放一定的内存空间,延迟OOM。

    弱引用

    弱引用就是通过 WeakReference 包装了一下,使用如下:

    1. WeakReference weakReference = new WeakReference<>(new Order());
    2. // 获取弱引用的值
    3. System.out.println(weakReference.get());

    弱引用和软引用最大的区别就是无论内存是否足够,弱引用都会被GC回收

    1. /**
    2. * 弱引用
    3. */
    4. public class WeakReferenceTest {
    5. public static void main(String[] args) {
    6. WeakReference weakReference = new WeakReference<>(new Order());
    7. // 获取弱引用的值
    8. System.out.println(weakReference.get());
    9. // 垃圾回收
    10. System.gc();
    11. try {
    12. Thread.sleep(200);
    13. } catch (InterruptedException e) {
    14. e.printStackTrace();
    15. }
    16. // 获取弱引用的值
    17. System.out.println(weakReference.get());
    18. }
    19. }
    运行结果:
    1. com.test.soft.Order@677327b6
    2. order对象被垃圾回收
    3. null
    弱引用在 WeakHashMap , ThreadLocal 中有使用到。

    虚引用

    Phantom 虚幻的

    • 英[ˈfæntəm]

    虚引用又叫做幻影引用,它需要配合一个队列来使用,但是我们 无法通过虚引用来获取一个对象的真实引用 ,我们来看下面的代码

    1. ReferenceQueue referenceQueue = new ReferenceQueue<>();
    2. // 虚引用在垃圾回收的时候,会被放到一个队列里,这个队列供垃圾回收器做特殊处理
    3. PhantomReference phantomReference = new PhantomReference<>(new Order(), referenceQueue);
    4. System.out.println(phantomReference.get());// 这里输出结果为null

    上述代码第五行的输出结果居然为null,进入到源码里面会发现

    1. public class PhantomReference extends Reference {
    2. /**
    3. * Returns this reference object's referent. Because the referent of a
    4. * phantom reference is always inaccessible, this method always returns
    5. * null.
    6. *
    7. * @return null
    8. */
    9. public T get() {
    10. return null;
    11. }
    12. }

    这居然直接返回的null,虚引用不能通过get()方法获取到值,那到底有何存在的意义呢?

    我们来看下面的代码:

    1. 为了演示效果,还是把堆内存大小设置为20M,添加JVM参数 -Xmx20M
    2. new 一个 ReferenceQueue 队列,创建虚引用,将 new Order() , referenceQueue 作为参数传入
    3. 开启一个线程,不断的往 list 集合中放入数据,时间长了堆内存会满,触发垃圾回收
    4. 再开启一个线程,循环读取 ReferenceQueue 队列里面的值
    1. /**
    2. * 虚引用
    3. */
    4. public class PhantomReferenceTest {
    5. public static void main(String[] args) {
    6. List list = new ArrayList<>();
    7. ReferenceQueue referenceQueue = new ReferenceQueue<>();
    8. // 虚引用在垃圾回收的时候,会被放到一个队列里,这个队列供垃圾回收器做特殊处理
    9. PhantomReference phantomReference = new PhantomReference<>(new Order(), referenceQueue);
    10. System.out.println(phantomReference.get());
    11. new Thread(() -> {
    12. while (true) {
    13. // 不断往list添加数据。内存会满,此时会进行垃圾回收,虚引用就会被放在队列里面
    14. list.add(new byte[1024 * 1024]);
    15. System.out.println(phantomReference.get());
    16. }
    17. }).start();
    18. new Thread(() -> {
    19. while (true){
    20. // 从队列里面读数据
    21. Referenceextends Order> poll = referenceQueue.poll();
    22. if (poll != null){
    23. System.out.println("虚引用对象被JVM回收" + poll);
    24. }
    25. }
    26. }).start();
    27. try {
    28. Thread.sleep(1000);
    29. } catch (InterruptedException e) {
    30. e.printStackTrace();
    31. }
    32. }
    33. }
    34. 复制代码
    35. 输出结果:
    36. null
    37. null
    38. null
    39. null
    40. null
    41. null
    42. null
    43. null
    44. null
    45. null
    46. null
    47. null
    48. null
    49. order对象被垃圾回收
    50. null
    51. null
    52. null
    53. null
    54. null
    55. 虚引用对象被JVM回收java.lang.ref.PhantomReference@12bc0828
    56. Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
    57. at com.test.soft.PhantomReferenceTest.lambda$main$0(PhantomReferenceTest.java:29)
    58. at com.test.soft.PhantomReferenceTest$$Lambda$1/1831932724.run(Unknown Source)
    59. at java.lang.Thread.run(Thread.java:748)
    60. 虚引用特点:

      当垃圾回收器准备回收一个对象,如果发现它还有虚引用,那么就会在回收该对象之前,把这个虚引用加入到与之关联的 ReferenceQueue 中。在 NIO 中,就用了虚引用来管理堆外内存。

    61. 相关阅读:
      win10安装Adobe 2022全新体验安装教程 你觉得到底香不香
      vmware esxi 7 直通GPU配置
      DRM全解析 —— CRTC详解(3)
      11个Redis系列高频面试题,哪些你还不会?
      Python零基础速成班-第17讲-Python for Pandas Series对象,DataFrame对象和性质,统计分析及排序运
      docker保存镜像、打包tar、加载tar镜像
      计算机网络简答题
      【1++的C++进阶】之异常
      Git常用命令大全
      R语言进行相关矩阵分析及其网络可视化
    62. 原文地址:https://blog.csdn.net/guanshengg/article/details/126343426