• Java 对象的四种引用-强软弱虚


    Java 中对象的引用分为四种,可以让我们更好的保证程序运行时足够的内存,这也是面试时经常问到的题目,在此记录一下。

    一、强引用

    最开始学习的 Java 变量的声明方式其实就是强引用,这是最常用、最普遍的引用。

    String str = new String("Hello World");

    这其实就是强引用。如果一个对象具有强引用,GC 绝不会回收它。当内存不够用时,JVM 宁愿抛出 OOM 异常也不会回收强引用对象。只有显式将强引用置为 null 才可以释放对象。

    如下代码,最终会抛出异常 Exception in thread "main" java.lang.OutOfMemoryError: Java heap space(自己测试时为了更快看到效果可以将 JVM 堆内存改小一下,如何设置可自行百度)。

    1. package com.qinshou.resume.reference;
    2. import java.util.ArrayList;
    3. import java.util.List;
    4. public class ReferenceDemo {
    5. private static boolean sStart = true;
    6. public static void main(String[] args) {
    7. strongReference();
    8. }
    9. public static void strongReference() {
    10. String str = new String("Hello World");
    11. List list = new ArrayList<>();
    12. int index = 0;
    13. long startTime = System.currentTimeMillis();
    14. while (sStart) {
    15. list.add(new String("Hello World " + (index++)));
    16. System.out.println("执行时间--->" + (System.currentTimeMillis() - startTime));
    17. try {
    18. Thread.sleep(1);
    19. } catch (InterruptedException e) {
    20. // TODO Auto-generated catch block
    21. e.printStackTrace();
    22. }
    23. }
    24. }
    25. }

    二、软引用

    对于软引用,如果内存空间足够,GC 就不会回收它,如果内存不足了才会回收。

    Java 中所有的引用都是 Reference 的子类,它是个抽象类,软引用对应的实现类是 SoftReference。我们可以将一个对象作为参数来创建 SoftReference 对象,这样就可以将这个对象的引用指定为软引用了。然后可以通过 SoftReference 对象的 get() 方法来获取传入的对象。

    1. String str = new String("Hello World");
    2. Reference softReference = new SoftReference<>(str);
    3. System.out.println("softReference.get--->" + softReference.get());

    软引用、弱引用、虚引用都可以搭配 ReferenceQueue 来使用,当所引用的对象被 GC 回收时,虚拟机会把这个引用加入到这个引用队列中。我们可以利用这个 ReferenceQueue 来跟踪对象的生命周期,可以通过 poll() 或者 remove() 方法来获取被回收的引用,这两个方法的区别是一个阻塞,一个非阻塞。因为从队列中获取到的引用是被回收的引用,所以调用它的 get() 方法得到的永远是 null。

    1. package com.qinshou.resume.reference;
    2. import java.lang.ref.Reference;
    3. import java.lang.ref.ReferenceQueue;
    4. import java.lang.ref.SoftReference;
    5. import java.util.ArrayList;
    6. import java.util.List;
    7. public class ReferenceDemo {
    8. private static boolean sStart = true;
    9. public static void main(String[] args) {
    10. softReference();
    11. }
    12. public static void softReference() {
    13. String str = new String("Hello World");
    14. Reference softReference = new SoftReference<>(str);
    15. System.out.println("softReference.get--->" + softReference.get());
    16. ReferenceQueue referenceQueue = new ReferenceQueue<>();
    17. List> list = new ArrayList<>();
    18. new Thread(new Runnable() {
    19. @Override
    20. public void run() {
    21. try {
    22. // poll() 方法会从队列中取去第一个引用,没有的话直接返回 null
    23. Referenceextends String> reference = referenceQueue.poll();
    24. System.out.println("reference--->" + reference);
    25. // remove() 方法会从队列中取去第一个引用,没有的话会阻塞当前线程,直到有被回收的引用
    26. reference = referenceQueue.remove();
    27. System.out.println("reference--->" + reference);
    28. sStart = false;
    29. // 从队列中获取的引用,调用 get() 方法获取真实对象的时候永远为 null,因为已经被回收掉了
    30. Object object = reference.get();
    31. System.out.println("object--->" + object);
    32. } catch (InterruptedException e) {
    33. e.printStackTrace();
    34. }
    35. }
    36. }).start();
    37. int index = 0;
    38. long startTime = System.currentTimeMillis();
    39. while (sStart) {
    40. list.add(new SoftReference<>(new String("Hello World " + (index++)), referenceQueue));
    41. System.out.println("执行时间--->" + (System.currentTimeMillis() - startTime));
    42. try {
    43. Thread.sleep(1);
    44. } catch (InterruptedException e) {
    45. // TODO Auto-generated catch block
    46. e.printStackTrace();
    47. }
    48. }
    49. }
    50. }

    三、弱引用

    弱引用跟软引用的区别的在于,软引用只会在内存空间不足时 GC 才会回收,而弱引用的话只要 GC 扫描到该引用所在内存区域,无论内存空间是否充足都会回收。

    1. package com.qinshou.resume.reference;
    2. import java.lang.ref.Reference;
    3. import java.lang.ref.ReferenceQueue;
    4. import java.lang.ref.WeakReference;
    5. import java.util.ArrayList;
    6. import java.util.List;
    7. public class ReferenceDemo {
    8. private static boolean sStart = true;
    9. public static void main(String[] args) {
    10. weakReference();
    11. }
    12. public static void weakReference() {
    13. String str = new String("Hello World");
    14. Reference weakReference = new WeakReference<>(str);
    15. System.out.println("weakReference.get--->" + weakReference.get());
    16. ReferenceQueue referenceQueue = new ReferenceQueue<>();
    17. List> list = new ArrayList<>();
    18. new Thread(new Runnable() {
    19. @Override
    20. public void run() {
    21. try {
    22. Referenceextends String> reference = referenceQueue.remove();
    23. sStart = false;
    24. } catch (InterruptedException e) {
    25. e.printStackTrace();
    26. }
    27. }
    28. }).start();
    29. int index = 0;
    30. long startTime = System.currentTimeMillis();
    31. while (sStart) {
    32. list.add(new WeakReference<>(new String("Hello World " + (index++)), referenceQueue));
    33. System.out.println("执行时间--->" + (System.currentTimeMillis() - startTime));
    34. try {
    35. Thread.sleep(1);
    36. } catch (InterruptedException e) {
    37. // TODO Auto-generated catch block
    38. e.printStackTrace();
    39. }
    40. }
    41. }
    42. }

    上述代码的 while 循环,退出得会比使用软引用时要快很多。在 Android 中,使用弱引用是防止内存泄漏的一个常见手段。

    四、虚引用

    虚引用又称幽灵引用、影子引用,它无法通过 get 方法获取到实例,如果一个对象仅持有虚引用,那它跟没有引用一样。虚引用主要跟 ReferenceQueue 一起搭配使用,用来跟踪对象被 GC 回收。

    1. package com.qinshou.resume.reference;
    2. import java.lang.ref.PhantomReference;
    3. import java.lang.ref.Reference;
    4. import java.lang.ref.ReferenceQueue;
    5. import java.util.ArrayList;
    6. import java.util.List;
    7. public class ReferenceDemo {
    8. private static boolean sStart = true;
    9. public static void main(String[] args) {
    10. phantomReference();
    11. }
    12. public static void phantomReference() {
    13. String str = new String("Hello World");
    14. PhantomReference phantomReference = new PhantomReference<>(str, null);
    15. System.out.println("phantomReference.get--->" + phantomReference.get());
    16. ReferenceQueue referenceQueue = new ReferenceQueue<>();
    17. List> list = new ArrayList<>();
    18. new Thread(new Runnable() {
    19. @Override
    20. public void run() {
    21. try {
    22. Referenceextends String> reference = referenceQueue.remove();
    23. sStart = false;
    24. } catch (InterruptedException e) {
    25. e.printStackTrace();
    26. }
    27. }
    28. }).start();
    29. int index = 0;
    30. long startTime = System.currentTimeMillis();
    31. while (sStart) {
    32. list.add(new PhantomReference<>(new String("Hello World " + (index++)), referenceQueue));
    33. System.out.println("执行时间--->" + (System.currentTimeMillis() - startTime));
    34. try {
    35. Thread.sleep(1);
    36. } catch (InterruptedException e) {
    37. // TODO Auto-generated catch block
    38. e.printStackTrace();
    39. }
    40. }
    41. }
    42. }

    五、总结

    合理使用 Java 提供的各种引用,可以更好的控制程序内存,优化我们的程序,减少 OOM 的风险。比如在设计一些缓存机制的时候,将缓存对象设置为软引用或虚引用,既可以实现高速缓存,又能避免一些内存问题。

  • 相关阅读:
    QT6不支持QDesktopWidget包含头文件报错Qt 获取设备屏幕大小
    Python 教程之 Numpy—— 数据类型对象
    身份证实名认证API接口,选择的时候应该注意什么?
    象棋中的马跳步问题
    专业直播录屏软件推荐,让画面更清晰!
    基于JAVA平时成绩管理系统计算机毕业设计源码+数据库+lw文档+系统+部署
    Python 下载大文件,哪种方式速度更快
    神经网络一般训练次数,神经网络训练时间
    数据结构之LinkedList与链表(上)
    【endnote】如何将参考文献放到想放的位置
  • 原文地址:https://blog.csdn.net/zgcqflqinhao/article/details/128087714