JVM如何在实践中收集SoftReferences?

Dav*_* L. 17 java caching jvm soft-references

我有一个在JVM中运行的两个单独的缓存(一个由第三方库控制),每个缓存都使用软引用.我希望JVM在由库控制的缓存之前清除我的受控缓存.SoftReference javadoc指出:

在虚拟机抛出OutOfMemoryError之前,保证已清除对软可访问对象的所有软引用.否则,不会对清除软引用的时间或清除对不同对象的一组此类引用的顺序施加约束.但是,鼓励虚拟机实现偏向清除最近创建或最近使用的软引用.

该类的直接实例可用于实现简单的缓存; 此类或派生的子类也可用于更大的数据结构,以实现更复杂的高速缓存.只要软引用的引用是强可达的,即实际上是在使用中,软引用就不会被清除.因此,复杂的高速缓存可以例如通过保持对这些条目的强烈指示来防止其最近使用的条目被丢弃,留下剩余的条目由垃圾收集器决定丢弃.

常见的JVM实现(尤其是HotSpot)如何在实践中处理SoftReferences?他们是否"反对清除最近创建或最近使用的软参考",这是由规范所鼓励的?

Edw*_*uck 7

看起来它可以调音,但事实并非如此.并发标记扫描收集器挂起在默认堆的实现上must_clear_all_soft_refs(),显然只有true在执行时才会执行_last_ditch_collection.

bool GenCollectedHeap::must_clear_all_soft_refs() {
  return _gc_cause == GCCause::_last_ditch_collection;
}
Run Code Online (Sandbox Code Playgroud)

虽然正常处理失败的分配有三个连续调用堆的do_collect方法,在CollectorPolicy.cpp

HeapWord* GenCollectorPolicy::satisfy_failed_allocation(size_t size,
                                                    bool   is_tlab) {
Run Code Online (Sandbox Code Playgroud)

哪个尝试收集,尝试重新分配,尝试扩展堆,如果失败,然后作为最后的努力,尝试收集清除软引用.

关于最后一个集合的评论非常有说服力(并且唯一一个触发清算软件的人)

  // If we reach this point, we're really out of memory. Try every trick
  // we can to reclaim memory. Force collection of soft references. Force
  // a complete compaction of the heap. Any additional methods for finding
  // free memory should be here, especially if they are expensive. If this
  // attempt fails, an OOM exception will be thrown.
  {
    IntFlagSetting flag_change(MarkSweepAlwaysCompactCount, 1); // Make sure the heap is fully compacted

    gch->do_collection(true             /* full */,
                       true             /* clear_all_soft_refs */,
                       size             /* size */,
                       is_tlab          /* is_tlab */,
                       number_of_generations() - 1 /* max_level */);
  }
Run Code Online (Sandbox Code Playgroud)

---编辑以回应显而易见的,我描述的是弱引用,而不是软引用---

在实践中,我认为只有在调用JVM进行垃圾收集以响应它们试图避免使用时,才会"不"遵循SoftReferences OutOfMemoryError.

为了使SoftReferences与所有四个Java 1.4垃圾收集器兼容,并与新的G1收集器兼容,决策必须仅依赖于可达性确定.当收割和压缩发生时,判断对象是否可达是为时已晚.这表明(但不要求)存在集合"上下文",其基于堆中的可用内存可用性来确定可达性.SoftReference在尝试遵循它们之前,这样的背景必须表明不遵循s.

由于OutOfMemoryError避免垃圾收集是以完全收集,世界各地的方式特别安排的,因此SoftReference在集合发生之前堆管理器设置"不跟随"标志的情况并不难想象.

---好的,所以我决定"必须以这种方式工作"的答案还不够好---

从源代码src/share/vm/gc_implementation/concurrentMarkSweep/vmCMSOperations.cpp(亮点是我的)

实际"做"垃圾收集的操作:

  170 void VM_GenCollectFullConcurrent::doit() {
Run Code Online (Sandbox Code Playgroud)

我们最好是一个VM线程,否则一个"程序"线程就是垃圾收集!

  171   assert(Thread::current()->is_VM_thread(), "Should be VM thread");
Run Code Online (Sandbox Code Playgroud)

我们是并发收集者,所以我们最好同时安排!

  172   assert(GCLockerInvokesConcurrent || ExplicitGCInvokesConcurrent, "Unexpected");
  173 
Run Code Online (Sandbox Code Playgroud)

抓住堆(其中包含GCCause对象).

  174   GenCollectedHeap* gch = GenCollectedHeap::heap();
Run Code Online (Sandbox Code Playgroud)

检查我们是否需要一个前景"年轻"的集合

  175   if (_gc_count_before == gch->total_collections()) {
  176     // The "full" of do_full_collection call below "forces"
  177     // a collection; the second arg, 0, below ensures that
  178     // only the young gen is collected. XXX In the future,
  179     // we'll probably need to have something in this interface
  180     // to say do this only if we are sure we will not bail
  181     // out to a full collection in this attempt, but that's
  182     // for the future.
Run Code Online (Sandbox Code Playgroud)

程序线程是否不干涉堆?

  183     assert(SafepointSynchronize::is_at_safepoint(),
  184       "We can only be executing this arm of if at a safepoint");
Run Code Online (Sandbox Code Playgroud)

从堆中获取垃圾收集原因(此收集的原因).

  185     GCCauseSetter gccs(gch, _gc_cause);
Run Code Online (Sandbox Code Playgroud)

完整收集年轻空间

请注意,他传递了堆的must_clear_all_soft_refs标志的值必须在OutOfMemory场景中设置为true,并且在任何一种情况下都指示"do_full_collection"不遵循软引用

  186     gch->do_full_collection(gch->must_clear_all_soft_refs(),
  187                             0 /* collect only youngest gen */);
Run Code Online (Sandbox Code Playgroud)

_gc_cause是一个枚举,这是(在这里猜测)设置为_allocation_failure第一次尝试避免OutOfMemoryError_last_ditch_collection失败后(尝试收集瞬态垃圾)

快速浏览一下内存"堆"模块,可以看出do_full_collection哪些调用do_collection软引用被明确清除(在"正确"条件下)

  480   ClearedAllSoftRefs casr(do_clear_all_soft_refs, collector_policy());
Run Code Online (Sandbox Code Playgroud)

---对于那些想要了解弱引用的人来说,原帖如下

在Mark和Sweep算法中,主线程遵循软引用(因此除非另一个分支可以通过非软引用到达它,否则不会标记.

在复制算法中,不会复制对象软引用指向(除非它们通过不同的非软引用到达).

基本上,当遵循"主"执行线程的引用Web时,遵循软引用.这允许他们的对象被垃圾收集,就好像他们没有指向它们的引用一样.

值得一提的是,软引用几乎从不单独使用.它们通常用于设计要对对象进行多次引用的对象,但只需要清除一个引用来触发垃圾回收(为了便于维护容器,或者运行时不需要查找昂贵的引用) .