Java软引用对象的创建以及对象回收

举例说明如何创建软引用对象。

在 Java 中,软引用是通过 java.lang.ref.SoftReference 类来实现的。

示例代码:

java 复制代码
import java.lang.ref.SoftReference;

public class SoftReferenceExample {

    public static void main(String[] args) {
        // 1. 创建一个普通对象
        MyLargeObject strongRefObject = new MyLargeObject("Initial Data");
        System.out.println("原始对象 (强引用) 创建成功: " + strongRefObject.getData());

        // 2. 创建一个软引用指向这个对象
        // SoftReference<MyLargeObject> softRef = new SoftReference<>(strongRefObject);
        // 在创建软引用后,可以将强引用置为 null,这样 MyLargeObject 实例就只剩下软引用了
        SoftReference<MyLargeObject> softRef = new SoftReference<>(strongRefObject);
        strongRefObject = null; // 将强引用置为 null

        System.out.println("软引用创建成功。");

        // 3. 尝试通过软引用获取对象
        MyLargeObject retrievedObject = softRef.get();
        if (retrievedObject != null) {
            System.out.println("通过软引用获取到对象: " + retrievedObject.getData());
        } else {
            System.out.println("通过软引用获取对象失败,对象可能已被回收。");
        }

        // 模拟内存紧张的情况,强制进行垃圾回收
        // 在实际应用中,JVM 会根据内存情况自动决定何时回收软引用对象
        // 这里只是为了演示效果,强制执行 GC,但不能保证一定回收
        System.out.println("\n尝试进行垃圾回收...");
        System.gc(); // 提示 JVM 进行垃圾回收
        try {
            Thread.sleep(100); // 等待一小段时间,让GC有机会执行
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }


        // 4. 再次尝试通过软引用获取对象
        MyLargeObject retrievedObjectAfterGC = softRef.get();
        if (retrievedObjectAfterGC != null) {
            System.out.println("GC 后通过软引用获取到对象: " + retrievedObjectAfterGC.getData());
        } else {
            System.out.println("GC 后通过软引用获取对象失败,对象已被回收。");
        }

        // 进一步模拟内存紧张,例如创建大量对象
        System.out.println("\n模拟内存紧张,创建大量对象...");
        // 尝试创建大量对象,耗尽内存,迫使JVM回收软引用对象
        // 注意:这可能导致 OutOfMemoryError,具体取决于JVM的配置和可用内存
        java.util.List<byte[]> memoryEater = new java.util.ArrayList<>();
        try {
            for (int i = 0; i < 1000; i++) { // 创建1000个1MB的字节数组
                memoryEater.add(new byte[1024 * 1024]); // 1MB
            }
        } catch (OutOfMemoryError e) {
            System.out.println("模拟内存紧张成功,发生 OutOfMemoryError。");
        }


        // 5. 在极度内存紧张后,再次尝试通过软引用获取对象
        MyLargeObject retrievedObjectAfterOOM = softRef.get();
        if (retrievedObjectAfterOOM != null) {
            System.out.println("内存紧张后通过软引用获取到对象: " + retrievedObjectAfterOOM.getData());
        } else {
            System.out.println("内存紧张后通过软引用获取对象失败,对象已被回收。");
        }
    }
}

// 模拟一个较大的对象,以便垃圾回收器更容易注意到它
class MyLargeObject {
    private String data;
    // 假设这个对象内部还有一些其他数据,占用内存
    private byte[] largeArray = new byte[1024 * 10]; // 10KB

    public MyLargeObject(String data) {
        this.data = data;
        System.out.println("MyLargeObject created with data: " + data);
    }

    public String getData() {
        return data;
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("MyLargeObject (" + data + ") is being finalized.");
        super.finalize();
    }
}

代码解释:

  1. MyLargeObject strongRefObject = new MyLargeObject("Initial Data");

    • 首先,我们创建了一个 MyLargeObject 的实例,并用一个普通的变量 strongRefObject 强引用它。此时,MyLargeObject 实例是强可达的。
  2. SoftReference<MyLargeObject> softRef = new SoftReference<>(strongRefObject);

    • 接着,我们创建了一个 SoftReference 对象,并将 strongRefObject 作为参数传给它的构造函数。这意味着 softRef 现在是一个软引用,指向 MyLargeObject 实例。
    • strongRefObject = null; 这一步非常关键。我们将原来的强引用 strongRefObject 置为 null。此时,MyLargeObject 实例就只剩下 softRef 这一个软引用了。这意味着它现在是"软可达"的。
  3. MyLargeObject retrievedObject = softRef.get();

    • 通过 softRef.get() 方法,我们可以尝试获取软引用指向的实际对象。
    • 如果对象还没有被垃圾回收器回收,get() 方法会返回该对象。
    • 如果对象已经被回收,get() 方法会返回 null
  4. System.gc(); 和模拟内存紧张

    • System.gc() 只是一个建议 JVM 执行垃圾回收的操作,JVM 不一定会立即执行,也不保证会回收软引用对象。
    • 为了更可靠地演示软引用在内存紧张时被回收的特性,我们通常需要模拟创建大量对象来耗尽内存。当 JVM 内存不足时,它会优先回收软引用指向的对象。

运行结果分析:

  • strongRefObject = null; 之后,MyLargeObject 实例就只剩下软引用了。
  • 在内存充足的情况下,softRef.get() 仍然可以获取到 MyLargeObject 实例。
  • 当 JVM 内存紧张(例如,通过创建大量对象来模拟),垃圾回收器会回收 MyLargeObject 实例。此时,再次调用 softRef.get() 就会返回 null
  • finalize() 方法会在对象被垃圾回收前调用,可以观察到其输出,进一步证明对象已被回收。

这个例子展示了软引用如何在内存紧张时自动释放对象,这对于实现缓存机制非常有用,既能提高性能,又能避免 OutOfMemoryError

手动执行 strongRefObject = null; 之后,softRef.get() 获取到的不一定是 null

让我来详细解释一下:

  1. strongRefObject = null; 的作用:

    • 这一行代码的作用是断开 strongRefObject 这个变量对 MyLargeObject 实例的强引用
    • 不会立即触发垃圾回收
    • 它只是使得 MyLargeObject 实例现在只剩下软引用 softRef 指向它 。此时,该 MyLargeObject 实例变为了"软可达"状态。
  2. 软引用对象的回收时机:

    • 软引用指向的对象,在内存充足时不会被回收
    • 只有当 JVM 即将耗尽内存(即内存紧张)时,垃圾回收器才会考虑回收这些软引用对象。
  3. softRef.get() 的行为:

    • softRef.get() 方法会尝试获取软引用指向的实际对象。
    • 如果该对象尚未被垃圾回收器回收 (因为内存还充足,或者垃圾回收器还没来得及回收),那么 get() 方法就会返回该对象实例。
    • 只有当该对象已经被垃圾回收器回收 后,get() 方法才会返回 null

所以,在 strongRefObject = null; 之后,MyLargeObject 实例虽然没有了强引用,但它仍然存在于内存中,并且通过 softRef 仍然是可访问的,直到 JVM 认为内存不足并决定回收它。

strongRefObject = null; 之后,以及甚至在第一次 System.gc() 之后,只要内存还充足,softRef.get() 仍然能够获取到对象。只有当模拟了内存紧张,JVM 真正开始回收软引用对象时,softRef.get() 才会返回 null

相关推荐
小成202303202652 小时前
Linux高级02
linux·开发语言
camellias_2 小时前
【无标题】
java·tomcat
知行合一。。。2 小时前
Python--04--数据容器(总结)
开发语言·python
咸鱼2.02 小时前
【java入门到放弃】需要背诵
java·开发语言
ZK_H2 小时前
嵌入式c语言——关键字其6
c语言·开发语言·计算机网络·面试·职场和发展
A.A呐2 小时前
【C++第二十九章】IO流
开发语言·c++
椰猫子2 小时前
Java:异常(exception)
java·开发语言
lifewange2 小时前
pytest-类中测试方法、多文件批量执行
开发语言·python·pytest
cmpxr_3 小时前
【C】原码和补码以及环形坐标取模算法
c语言·开发语言·算法
2401_827499993 小时前
python项目实战09-AI智能伴侣(ai_partner_5-6)
开发语言·python