弱引用和Scala REPL

4

我想尝试使用scala.ref.WeakReference。但是,在尝试实现这个大事之前,我想在Scala控制台中检查其行为。我尝试了一些方法,但无法使我的对象被取消引用。以下是我的其中一个尝试:

> class A
defined class A

> class B(var value: A)
defined class B

> new B(new A)
res0: B = B@c8aeb3

> new scala.ref.WeakReference(res0.value)
res1: scala.ref.WeakReference[A] = scala.ref.WeakReferenceWithWrapper@16a5d72

> res0.value = new A

> res1.get // Here I hope to get None
res3: Option[A] = Some(A@135707c)
< p > oxbow_lakes 提供了另一种尝试。< /p> < p > 我也曾经尝试显式运行垃圾回收器(调用java.lang.System.gc),但都无济于事。 < /p> < p > 有没有办法引用res1的内容?< /p>

正如您所看到的,我们对REPL的功能、REPL所说的功能以及REPL应该执行的功能以及GC何时跳过收集未引用对象都感到非常困惑。有没有可能您可以编写一个Scala脚本在文件中测试您想要的行为,并查看是否有效,这样您就不必担心REPL打印其结果的差异了? - Ken Bloom
4个回答

5
Welcome to Scala version 2.8.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_22).
Type in expressions to have them evaluated.
Type :help for more information.

scala> class A  
defined class A

scala> class B(var value: A)
defined class B

scala> new B(new A)
res0: B = B@4223d9b

scala> new scala.ref.WeakReference(res0.value)
res1: scala.ref.WeakReference[A] = scala.ref.WeakReferenceWithWrapper@20eb607d

scala> res0.value = new A

scala> System gc

scala> res1 get
res3: Option[A] = None

顺便说一下,如果我将其作为脚本运行而没有显式地使用“System gc”,它也不会删除引用。所以对我来说,这不是REPL的问题,而只是弱引用和垃圾回收器工作方式的问题。

这绝对是一个REPL的怪癖。你的解决方案只是让REPL不会直接引用我在答案中指出的var的第一个值。但是REPL将保持对你给出的res0.value第二个值的直接引用,所以你并没有完全解决一般实验的问题。 - Ken Bloom
好的,我只是遵循了原来的问题... - Debilski
它没有保留对 res0.value 的引用,因为赋值 res0.value = new A 返回了 ()。REPL 实际上从未看到它。如果它保留了一个引用,它将在结果字符串中向您显示。 - Debilski
当您要求REPL运行b = new A时,它实际上运行的是b = new A; val synthvar$0 = res0.value。它使用这个来打印赋值语句的结果,显示为b :A = A@92524b0。(是的,我正在使用2.8.1.final。) - Ken Bloom
1
你是对的。REPL 对 res0.value = new Ab = new A 做了不同的处理。在 res0.value 的情况下,它不会保留引用。闭包解决方案是不必要的(至少目前是这样)。 - Ken Bloom
显示剩余2条评论

1

使用scala -Xprint:parser运行您的代码,您将看到即使在重新分配后,var的旧值仍然被引用。

我要在这里简化事情,只运行两行代码:

var b=1
b=2

这是Scala打印的内容:

scala> var b=1
[[syntax trees at end of parser]]// Scala source: <console>
package <empty> {
  object line2$object extends scala.ScalaObject {
    def <init>() = {
      super.<init>();
      ()
    };
    object $iw extends scala.ScalaObject {
      def <init>() = {
        super.<init>();
        ()
      };
      object $iw extends scala.ScalaObject {
        def <init>() = {
          super.<init>();
          ()
        };
        var b = 1                                ///// YOUR CODE HERE
      }
    }
  }
}

[[syntax trees at end of parser]]// Scala source: <console>
package <empty> {                                /////THIS IS AN object
                                                 /////SO PRESUMABLY IT CAN'T BE GC'ED
  object RequestResult$line2$object extends scala.ScalaObject {
    def <init>() = {
      super.<init>();
      ()
    };
    lazy val scala_repl_value = {                 /////THIS LAZY VAL
      scala_repl_result;                          /////WILL REFERENCE THE OLD VALUE
      line2$object.$iw.$iw.b                      /////EVEN AFTER YOU REASSIGN THE var
    };
    val scala_repl_result: String = {
      line2$object.$iw.$iw;
      "".$plus("b: Int = ").$plus(scala.runtime.ScalaRunTime.stringOf(line2$object.$iw.$iw.b))
    }
  }
}

b: Int = 1

scala> b=2
[[syntax trees at end of parser]]// Scala source: <console>
package <empty> {
  object line3$object extends scala.ScalaObject {
    def <init>() = {
      super.<init>();
      ()
    };
    object $iw extends scala.ScalaObject {
      def <init>() = {
        super.<init>();
        ()
      };
      import line2$object.$iw.$iw.b;              ///// I DON'T THINK THIS (ORDINARILY ILLEGAL)
                                                  ///// import CONTRIBUTES TO THE PROBLEM
      object $iw extends scala.ScalaObject {
        def <init>() = {
          super.<init>();
          ()
        };
        b = 2;                                    /////YOUR CODE HERE
        val synthvar$0 = b
      }
    }
  }
}

[[syntax trees at end of parser]]// Scala source: <console>
package <empty> {
  object RequestResult$line3$object extends scala.ScalaObject {
    def <init>() = {
      super.<init>();
      ()
    };
    lazy val scala_repl_value = {
      scala_repl_result;
      line3$object.$iw.$iw.synthvar$0
    };
    val scala_repl_result: String = {
      line3$object.$iw.$iw;
      "".$plus("b: Int = ").$plus(line3$object.$iw.$iw.synthvar$0).$plus("\012")
    }
  }
}

b: Int = 2

编辑:补充Debilski的回答,我认为下面的解决方案可以让您随意重新分配变量,而不会使REPL保留对旧值的引用:

class A
class B{
   var _value:A = new A
   def value = _value
   def pleaseUpdate( closure: B => Unit ) = closure(this)
}

将你的容器对象定义为:

val b=new B

每当你想要更新其中的变量:

b.pleaseUpdate( _._value = new A )

我无法从这个代码片段中确定,但是如果我没记错的话,当Scala在变量上构建闭包时,它会将其装箱为可变形式,并且所有引用都指向该盒子。这样,所有不同的引用都可以看到对变量值的相同更新。因此,如果它捕获了该变量,我认为即使将该变量设置为null,也会防止盒子被回收,但不会防止其所引用的对象被回收。但是,我已经很久没有深入研究过这个问题了... - Erik Engbrecht
@Erik:我猜这取决于何时评估scala_repl_value - Ken Bloom

0
我的想法是明确地使用 var 并将其设置为 null:
scala> var b = new B(new A)
b: B = B@45033fb5

scala> new scala.ref.WeakReference(b.value)
res0: scala.ref.WeakReference[A] = scala.ref.WeakReferenceWithWrapper@6a7be687

scala> b = null
b: B = null

scala> res0.get
res1: Option[A] = Some(A@79f71773)

但它仍然不起作用:REPL 可能正在幕后处理一些东西,这使其保持引用。因此,我建议不要使用它来测试对 Reference 的使用。


这是另一次尝试,但它也不起作用。我还担心 REPL 会保留对象的不明引用。 - Nicolas
1
是的 - 我的测试也显示了同样的结果。出于完整性考虑,我已经保留了答案。 - oxbow_lakes

0

来自Java API文档:

调用gc方法建议Java虚拟机花费精力回收未使用的对象,以便使它们当前占用的内存可用于快速重用。当从方法调用返回控件时,Java虚拟机已尽最大努力从所有丢弃的对象中回收空间。

根据我的经验,这意味着它很少触发真正完整的垃圾回收,特别是如果没有可用内存的压力。如果你给它施加压力,它就会被回收。我认为你期望GC提供比它实际提供的更确定性的行为。

Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_22).
Type in expressions to have them evaluated.
Type :help for more information.

scala> var s = new String("Hello, world!")         
s: java.lang.String = Hello, world!

scala> import scala.ref.WeakReference
import scala.ref.WeakReference

scala> val w = new WeakReference(s)  
w: scala.ref.WeakReference[java.lang.String] = scala.ref.WeakReferenceWithWrapper@663f3fbd

scala> s = null
s: java.lang.String = null

scala> Array.ofDim[Byte](1024*1024)
res1: Array[Byte] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
scala> Array.ofDim[Byte](1024*1024)
res2: Array[Byte] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
scala> Array.ofDim[Byte](1024*1024)
res3: Array[Byte] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
scala> Array.ofDim[Byte](1024*1024)
res4: Array[Byte] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
scala> Array.ofDim[Byte](1024*1024)
res5: Array[Byte] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
scala> Array.ofDim[Byte](1024*1024)
res6: Array[Byte] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
scala> System.gc

scala> var g = w.get
g: Option[java.lang.String] = None

啥???我的答案错了还是你的答案错了。看看我的,告诉我哪里理解有误了。 - Ken Bloom
我在2.7.7、2.8.0和2.8.1中看到了与Erik相同的行为。(尽管在2.7.7中,WeakReference将在没有System.gc的情况下被删除-但这可能是巧合。) - Debilski
这里涉及到很多不确定性。有没有人把我的转录复制并粘贴到2.8.1 REPL中?我现在没有时间去安装。 - Erik Engbrecht
你说得对。即使在2.8.1版本中,该变量已被删除,尽管“-Xprint:parser”似乎显示它仍保留了一个引用。我不确定我还理解发生了什么。 - Ken Bloom
@Debilsky - 我不得不添加数组内容才能让它在我的机器上运行,并且我必须调整执行次数和数组大小的数量,以使其产生所需的结果。 - Erik Engbrecht
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接