【问题标题】:Java SoftReference guarantee failingJava SoftReference 保证失败
【发布时间】:2014-01-20 23:37:18
【问题描述】:

JDK 7 文档对SoftReference 有这样的说法:

“所有对软可访问对象的软引用保证在虚拟机抛出 OutOfMemoryError 之前被清除。”

但是,在我的测试程序中,我一直看到OutOfMemoryError(下面的“陌生人行为”部分除外)

// RefObjectTest.java

import java.util.*;
import java.lang.ref.*;

public class RefObjectTest {

    public static void main(String[] args) {

        ArrayList<byte[]> leaks = new ArrayList<>();

        byte[] obj = new byte[10 * 1024 * 1024];

        SoftReference<byte[]> ref = new SoftReference<>(obj);

        // WeakReference is supposed to be eagerly GC'ed, but I see no
        // difference in terms of program behavior: still get OOME.
        //
        // WeakReference<byte[]> ref = new WeakReference<>(obj);

        obj = null;

        while(true) {
            byte[] x = ref.get();
            if(x == null) {
                System.out.println("Referent stands garbage collected!!");
                break;
            } else {
                System.out.println("Referent still alive.");
            }

            // Leak memory in small, 10k increments. If soft reference
            // worked the way it's advertized, then just before the OOME, the 
            // 10MB referent should have been GC'ed to accomodate this small
            // 10K new memory request. But it doesn't appear to work that way!

//          try {
                leaks.add(new byte[10 * 1024]);
//          } catch(OutOfMemoryError e) {
//              System.out.println(ref.get() == null ? "Referent cleared" : 
//                  "Referent still alive");
//          }

            // VERY STRANGE: If I re-instate the above try-catch block, then no OOME is
            // thrown, and the program keeps printing "Referent still alive" lines
            // forever until I finally kill it with a Ctrl+C.

            // Uncommenting gc() tends to delay the OOME in terms of time, 
            // but OOME eventually does happen, and after the same number of
            // iterations of this loop. 
            // 
            // System.gc();
        }
    }
}

这是输出:

$ java -Xmx15m RefObjectTest
Referent still alive.
  ...
Referent still alive.
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at RefObjectTest.main(RefObjectTest.java:38)

陌生人行为

非常奇怪的是,如果我重新设置 try-catch 块,程序似乎永远运行良好,并打印“Referent still alive”。一直排到我累了就杀了它。

$ java -version
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)
$ 
$ uname -a
Linux ida 3.10.11-100.fc18.x86_64 #1 SMP Mon Sep 9 13:06:31 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

我在这一切中缺少什么?

【问题讨论】:

    标签: java garbage-collection out-of-memory weak-references soft-references


    【解决方案1】:

    一点也不奇怪,分配失败引发异常。捕获异常意味着程序可以继续运行,不捕获异常意味着无法处理并且程序退出。

    在你的 while 循环中你正在做:

            byte[] x = ref.get();
    

    这是从您的软引用创建一个新的强引用。一旦你这样做,它就不再有资格收集。在继续进行新分配之前,您没有清除强引用。

    完成测试后,请x = null

    【讨论】:

    • 感谢您的快速回复! (我什至在我的测试代码中都搞砸了!)
    • 开头的byte[] obj 是另一个强引用,会阻止清除软引用。
    • 通常你会是对的,但是在 while(true) 之前那个值被设置为 null。
    猜你喜欢
    • 1970-01-01
    • 2015-02-05
    • 2015-10-25
    • 2013-12-20
    • 2016-08-21
    • 1970-01-01
    • 1970-01-01
    • 2023-03-06
    • 2020-08-21
    相关资源
    最近更新 更多