当前位置: 首页 > 面试题库 >

实际上,JVM如何收集SoftReferences?

顾乐池
2023-03-14
问题内容

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

在虚拟机引发OutOfMemoryError之前,确保已清除所有对软可访问对象的软引用。否则,在清除软引用的时间或清除对不同对象的一组此类引用的时间上将没有任何限制。但是,鼓励虚拟机实现偏向于清除最近创建或最近使用的软引用。

此类的直接实例可用于实现简单的缓存;此类或派生的子类也可以在较大的数据结构中使用,以实现更复杂的缓存。只要可以很容易地获得软引用的引用,即实际上正在使用该引用,则不会清除该软引用。因此,复杂的高速缓存可以例如通过保持对那些条目的强引用来防止其最近使用的条目被丢弃,而其余条目则由垃圾收集器来决定丢弃。

常见的JVM实现(尤其是HotSpot)如何在实践中处理SoftReferences?它们是否像规范所鼓励的那样“倾向于清除最近创建或最近使用的软引用”?


问题答案:

看起来它可能是可调的,但事实并非如此。并发标记清除收集器挂在默认堆的实现上,must_clear_all_soft_refs()该堆显然仅true在执行时才执行_last_ditch_collection

bool GenCollectedHeap::must_clear_all_soft_refs() {
  return _gc_cause == GCCause::_last_ditch_collection;
}

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

HeapWord* GenCollectorPolicy::satisfy_failed_allocation(size_t size,
                                                    bool   is_tlab) {

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

对最后一个集合的评论很有说服力(也是唯一一个触发清除软引用的评论)

  // 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 */);
  }

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

在实践中,我可以想象当调用JVM进行垃圾回收以响应它们试图避免时,SoftReferences只会被“不”跟随OutOfMemoryError

为了使SoftReferences与所有四个Java
1.4垃圾收集器以及新的G1收集器兼容,决策必须仅取决于可到达性确定。到收割和压实发生时,确定某个对象是否可触​​及为时已晚。这表明(但不要求)存在一个集合“上下文”,该集合根据堆中的可用内存可用性确定可达性。这样的上下文必须SoftReference在尝试关注之前指示不关注。

由于OutOfMemoryError回避垃圾收集是按全收集的“停止世界”的方式专门安排的,因此很难想象堆管理器SoftReference在收集发生之前设置“不遵循”标志的情况。

-–好的,所以我认为“必须以这种方式工作”的答案还不够好-

从源代码src / share / vm / gc_implementation / concurrentMarkSweep /
vmCMSOperations.cpp
(突出显示是我的)

实际“执行”垃圾收集的操作:

  170 void VM_GenCollectFullConcurrent::doit() {

我们最好成为VM线程,否则“程序”线程就是垃圾回收!

  171   assert(Thread::current()->is_VM_thread(), "Should be VM thread");

我们是并发收集器,因此最好同时进行调度!

  172   assert(GCLockerInvokesConcurrent || ExplicitGCInvokesConcurrent, "Unexpected");
  173

抓取堆(其中具有GCCause对象)。

  174   GenCollectedHeap* gch = GenCollectedHeap::heap();

检查是否需要前台“年轻”集合

  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.

程序线程是否不干预堆?

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

从堆中获取垃圾回收原因(此回收的原因)。

  185     GCCauseSetter gccs(gch, _gc_cause);

充分收集年轻空间

请注意,他传递了堆的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 */);

_gc_cause是一个枚举,_allocation_failure在第一次尝试中将其设置为(guesswork)以避免OutOfMemoryError_last_ditch_collection然后失败(尝试收集临时垃圾)

快速浏览一下内存“堆”模块,发现do_full_collection其中的do_collection软引用的调用已使用行显式清除(在“正确”条件下)

  480   ClearedAllSoftRefs casr(do_clear_all_soft_refs, collector_policy());

-–那些想要学习弱引用的人的原始帖子如下-

在标记和清除算法中,主线程 不会 遵循软引用(因此不会进行标记,除非其他分支可以通过非软引用到达软引用)。

在复制算法中,对象软引用指向的对象 不会被 复制(同样,除非其他非软引用到达了它们)。

基本上,从“主”执行线程中遵循引用Web时, 遵循软引用。这允许对它们的对象进行垃圾回收,就像它们没有指向它们的引用一样。

重要的是要提到,软引用几乎 永远不会
孤立地使用。它们通常用于设计要对该对象有多个引用的对象中,但是只需要清除一个引用即可触发垃圾回收(为便于维护容器或无需查找昂贵的引用的运行时性能) 。



 类似资料:
  • 从文档中可以看出,-Xss用于设置JVM的堆栈大小。但我对这一说法感到非常困惑。 在Java中,每个线程都有自己的堆栈。-Xss指定的数字是否: > 线程的每个堆栈的大小。e、 如果将-Xss设置为256K,则每个线程将有一个256K大的堆栈。因此,10个线程将总共使用2560K。 非常感谢你。 编辑: 谢谢你的回答。看起来像是上面的(2)senario-Xss指定特定线程的最大堆栈大小。 然后我

  • 问题内容: 编译器或OS如何区分sig_atomic_t类型和普通的int类型变量,并确保操作是原子的?两者都使用的程序具有相同的汇编代码。如何特别注意使操作原子化? 问题答案: 不是原子数据类型。仅仅是允许您在信号处理程序的上下文中使用的数据类型,仅此而已。因此最好将其名称理解为“相对于信号处理而言是原子的”。 为了保证与信号处理程序之间的通信,仅需要原子数据类型的属性之一,即读取和更新将始终看

  • 问题内容: 我有一个Applet,它使用URLConnection通过HTTP连接加载图像。我正在为所有连接设置setUseCaches(true),但仍然看不到任何缓存行为。我图像的HTTP标头具有合理的缓存设置。如果您查看错误4528599,则有一个相当神秘的陈述: Java插件的当前版本(1.3.1)仅检查浏览器缓存中名称以.jar或.class结尾的文件。我被告知,对于Java Plug-

  • 问题内容: 有人知道Spring实际上是如何引导的吗? 哪些实例由谁创建? 我真的很想知道谁创建了WebApplicationContext和ContextLoader实例。是Tomcat的作品吗? 问题答案: 用户 正在部署Web应用程序WAR 。 Servlet容器 (Tomcat)读取。 Servlet 容器*正在实例化Servlet上下文侦听器(如果定义在内部)。 * 使用应用程序上下文X

  • 问题内容: 首先,我要说我知道它是什么,做什么以及如何使用它。这个问题关系到它在引擎盖下的工作方式,我不希望出现“这就是如何用” 循环数组”的答案。 很长时间以来,我一直认为该方法可用于数组本身。然后,我发现了很多关于它可以与数组 副本 一起使用的事实的引用,从那时起,我一直以为这是故事的结尾。但是我最近对此事进行了讨论,经过一番实验后发现这实际上并非100%正确。 让我表明我的意思。对于以下测试

  • 每个java开发人员都知道,java对象不再使用时将被垃圾收集。我想知道JVM如何识别必须为垃圾收集选择的对象。(例如,如果我有10个对象。如果对10个对象中的2个进行垃圾收集,jvm将如何找到这两个对象)。 JVM使用标记和扫描算法(如果我是对的)。 1)例如我在下面提供字符串对象场景 //现在s1