空闲内存和垃圾回收器

3
我有一个JavaFX应用程序,显示一个带有多个选项卡的TabPane,就像一个普通的浏览器。
我通常在这些选项卡中加载一些网格。每次选择一个选项卡时,旧的选项卡内容就会被丢弃,新的选项卡就会被加载(从服务器上)。
问题是当你点击旧的选项卡时,必须等待它再次加载。我想要像现代浏览器一样拥有相同的行为,并保存我的应用程序中的内容。问题是,我不知道我的应用程序分配了多少内存,以及可以打开多少个选项卡。
基本上,我正在检查我还剩下多少内存(通过以下答案Java get available memory),如果我不够,我就释放所有选项卡内容。因此,我的应用程序能够使用800Mo的内存运行,但如果有人有6Go的内存,体验将更好。
在内存释放过程结束时,我手动调用垃圾回收器,因为我发现释放我的选项卡内容(允许它们被垃圾回收)不是立即的。我知道这很糟糕,但另一方面,我依赖于垃圾回收器。如果没有调用,那也没关系,我的选项卡内容最终会被收集起来。以下是所谓的方法:
/**
 * Before an action that may take some memory, we check how much memory we
 * have left. If memory is short, we go through the Tab in order to release
 * them if possible.
 */
public void verifyMemory() {
    long allocatedMemory = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
    long memoryLeft = Runtime.getRuntime().maxMemory() - allocatedMemory;
    System.out.println(memoryLeft/1000000);
    //We must release some memory
    if (memoryLeft < MEMORY_TRESHOLD) {
        LOGGER.warn("Memory treshold is hit, we must release memory");
        for (Tab tab : getTabs()) {
            if (tab instanceof MyTab) {
                ((MyTab) tab).destroyIfPossible();
            }
        }
        System.gc();
    }
}

你对我的解决方案有强烈的反对意见吗?或者有什么想法可以改进或帮助我实现目标吗?


是的,调用 System.gc() 方法并不保证会触发该方法。 - ΦXocę 웃 Пepeúpa ツ
1
是的,它并不是百分之百保证能够正常工作。但是,在大多数情况下它都可以正常运行。 - Boris Schegolev
事实是,我并不依赖它。如果被调用,那很好,如果没有被调用,也不是问题。这就是为什么我允许自己调用它的原因。 - Maxoudela
1
你看过SoftReference了吗? - biziclop
1个回答

2
处理这种情况的标准方法是构建一个缓存,使用SoftReference来保留未使用的对象在缓存中,直到GC决定必须释放那些内存为止。
确切的行为可以通过-XX:SoftRefLRUPolicyMSPerMB选项进行微调,但基本原则是可用的空闲内存越多,软引用对象就能被保存更长时间。(与弱引用不同,它们会在第一次机会时清除。)
(请注意,使用SoftReference存在几个问题,限制了它们作为一般缓存解决方案的实用性:例如它们跨整个VM共享,或者需要至少两个GC周期才能清除。但它们非常适合在独立应用程序中缓存一些大型对象。)
例如,Guava的CacheBuilder提供了创建这种类型缓存的功能。

问题在于,我需要清除的不仅仅是我的选项卡内容。我有一个围绕选项卡的生态系统,我想要全部清除或者什么都不清除。也许我可以将所有内容包装成软引用。 此外,我需要进行一些实时测试,以便查看这些软引用何时被清除。你说“必须释放”,但文档说这取决于垃圾收集器的“自行决定”。 - Maxoudela
@Maxoudela 你说得对,唯一的硬保证是在抛出 OutOfMemoryException 之前,GC 将清除所有软引用。但在我所知道的所有 JVM 实现中,软引用在实践中有足够长的生命周期,除非你的内存不足。而且好处是这是与垃圾收集器交互的标准方式。 - biziclop
这里有一篇文章解释了Oracle/OpenJDK中它是如何工作的。虽然它相当古老,但一些细节可能已经发生了变化,但并不会有太大的改变。 - biziclop

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