4

我是 Clojure 的新手,没有很多 Java 经验。

我正在开发一个系统,该系统是围绕将一个或多个输入惰性序列转换为新的惰性序列的函数构建的。例如,一个函数采用两个序列并产生输入之间“差异”的惰性序列。

在测试用例中,我验证序列是否已实现?在适当的时间,但我不知道如何验证我没有在某个地方“抓住头”,除了对一个巨大的序列进行操作并等待 OutOfMemoryError。我不喜欢这种方法,因为“巨大的序列”是任意的,并且测试需要很长时间才能运行。

我查找了有关 Java 垃圾收集器的信息,但找不到我要查找的内容(或不理解)。

建议?

4

3 回答 3

1

监控对象的 GC 资格的“正确”方法是使用一些java.lang.ref引用类型,最好是 aPhantomReference和 a ReferenceQueue

PhantomReferences 与其他java.lang.ref类的不同之处在于它们不能用于获取引用的目标;它们仅与ReferenceQueues 一起使用。此外,对象只有在最终确定后才被认为是虚拟可访问的。

样板看起来像这样:

(def rq (java.lang.ref.ReferenceQueue.))
(def pref (java.lang.ref.PhantomReference. [1 2] rq))
(future (.remove rq 0) (println :done))
(System/gc)
;;=> :done

最后一行显示未来的打印输出。

我有一个应该处理样板的小型图书馆,但是唉,它似乎需要一些维护工作......我会尽快解决它并在完成后发布一个链接。

于 2013-09-06T09:02:11.467 回答
0

(.totalMemory (Runtime/getRuntime))将跟踪 jvm 使用的总内存大小。

检查分配的内存,存储值,在数千个元素序列上运行代码,然后再次检查分配的内存应该得到你想要的。

也许有一种方法可以减少默认大小和分配粒度,以检查单元测试中内存使用量的增加。

于 2013-09-05T18:59:34.393 回答
0

一种可能的方法是将 a 存储WeakReference到 seq 中,然后在稍后的某个时间点,在应该丢弃对 seq 的所有引用之后,强制进行垃圾回收,取消引用 Wea​​kReference.get并查看它是否已被清除。例如:

(let [r (atom nil)
      s (atom nil)]
  (let [x (repeat 1000 1)]
    (reset! r (java.lang.ref.WeakReference. x))
    (reset! s (map (partial * 2) x)))

  ;; the lazy seq created by map has not yet been realized.
  (is (false? (realized? @s)))

  ;; Request garbage collection.
  (System/gc)

  ;; since it's not realized, the result from map still has a reference
  ;; to the head of its input seq.
  (is (false? (nil? (.get @r))))

  ;; Realize the seq returned from map.
  (dorun @s)
  (is (realized? @s))

  ;; Request garbage collection again.
  (System/gc)

  ;; Once the result from map has been realized, it should discard its reference
  ;; to the head of the input seq.
  (is (nil? (.get @r))))

但是请注意,在 Java 中测试与垃圾收集和内存分配相关的任何东西都是一门不精确的科学,因为这些东西是由 JVM 异步管理的。特别是,System.gc()建议JVM做垃圾回收;无法保证当该方法返回时弱引用将被清除,因此该测试可能会出现虚假失败。出于这个原因,许多风格检查器会将调用标记System.gc()为“巫毒”。

一般来说,最好的方法是编写可靠的代码,并且只有在很明显存在内存泄漏时才在VisualVM等分析器中进行调试。

于 2013-09-05T19:45:22.887 回答