如何在Java中强制进行垃圾回收?

270

在Java中是否可以强制进行垃圾回收?即使有些棘手,我知道System.gc();Runtime.gc();但它们只是建议进行GC。那么如何强制GC呢?


32
或许为什么需要强制执行GC提供一些背景会有帮助。通常在垃圾收集语言中,显式调用垃圾收集器是一个不好的做法。请注意保持原意,同时使翻译更加易懂,不要添加解释和其他内容。 - Justin Ethier
3
一些JVM可能提供多种垃圾回收方法,每种方法都有其优缺点。通常情况下,只需在启动时提示JVM,就可以避免出现特定的情况。请详细说明这种情景。 - Thorbjørn Ravn Andersen
3
使用命令 "jmap -histo:live <pid>" 时,是否会强制进行垃圾收集?可参考这个链接:https://dev59.com/Q2w15IYBdhLWcg3wv-ba - user2463941
9
这是强制进行垃圾回收的一个应用场景:我有一个带有30GB堆的服务器,其中约有12GB通常被使用(大约5M个对象)。每5分钟,服务器会花费大约一分钟执行一个复杂任务,其中大约使用了35M个额外的对象。几乎每小时都会触发几次完整的垃圾回收,总是在执行复杂任务时发生,并且会导致VM冻结10到15秒钟。我希望能够在不运行复杂任务的时间强制运行完整的垃圾回收;这样,它将只需要处理5M个活跃对象而不是40M个。 - Steve
5
@JustinEthier 有一个非常明显的情况,你可能想要强制进行GC,那就是单元测试涉及java.lang.ref.Reference类型层次结构的任何行为。 - Elias Vasylenko
显示剩余5条评论
25个回答

-2

请注意

方法调用System.runFinalizersOnExit(true)保证在Java关闭之前调用finalizer方法。但是,该方法本质上是不安全的并已被弃用。一种替代方法是使用方法Runtime.addShutdownHook添加“关闭钩子”。

Masarrat Siddiqui


关闭挂钩的缺陷在于它们很少实际起作用。强制终止不起作用,非零退出代码也不起作用,有时(官方)JVM根本不会运行它们所需的时间长。 - ThePyroEagle

-2

我想在这里补充一些内容。请注意,Java运行在虚拟机上而不是实际的机器上。虚拟机有其自己的与机器通信的方式。它可能因系统而异。现在,当我们调用GC时,我们要求Java的虚拟机调用垃圾回收器。

由于垃圾回收器在虚拟机中,我们无法强制它立即进行清理。相反,我们将我们的请求排队给垃圾回收器。这取决于虚拟机,在特定时间后(这可能因系统而异,通常是当分配给JVM的阈值内存已满时),实际机器将释放空间。 :D


第二段的第一句话是一个“非因果关系”。 - user207421
这个答案完全混淆了“Java虚拟机”(它是一个字节码解释器)和“虚拟机”(它是一种在另一个处理器上模拟处理器的方式)。而且,JVM并没有将GC委托给低级机器,它有自己的GC例程。 - toolforger

-2
在使用OracleJDK 10和G1 GC时,单个System.gc()的调用将导致GC清除旧的集合。我不确定GC是否立即运行。然而,即使在循环中多次调用System.gc(),GC也不会清除Young Collection。要让GC清理Young Collection,必须在循环中分配(例如new byte[1024]),而不调用System.gc()。由于某种原因,调用System.gc()会防止GC清理Young Collection。

-2

有一些间接的方法可以强制垃圾回收器。你只需要用临时对象填充堆,直到垃圾回收器执行为止。我已经创建了一个类,以这种方式强制垃圾回收器:

class GarbageCollectorManager {

    private static boolean collectionWasForced;
    private static int refCounter = 0;

    public GarbageCollectorManager() {
        refCounter++;
    }

    @Override
    protected void finalize() {
        try {
            collectionWasForced = true;
            refCounter--;
            super.finalize();   
        } catch (Throwable ex) {
            Logger.getLogger(GarbageCollectorManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public int forceGarbageCollection() {
        final int TEMPORARY_ARRAY_SIZE_FOR_GC = 200_000;
        int iterationsUntilCollected = 0;
        collectionWasForced = false;

        if (refCounter < 2) 
            new GarbageCollectorManager();

        while (!collectionWasForced) {
            iterationsUntilCollected++;
            int[] arr = new int[TEMPORARY_ARRAY_SIZE_FOR_GC];
            arr = null;
        }

        return iterationsUntilCollected;
    }

}

使用方法:

GarbageCollectorManager manager = new GarbageCollectorManager();
int iterationsUntilGcExecuted = manager.forceGarbageCollection();

我不知道这种方法有多少用处,因为它会不断填充堆,但如果您有关键任务应用程序,必须强制GC - 那么这可能是Java可移植的强制GC方式。


这个是否有效: "_" 在整数中? - Koray Tugay
1
是的,从Java SE 7开始,数字字面量中的下划线是有效的。例如,在整数中作为千位分隔符时非常有用。 - Agnius Vasiliauskas
3
你绝不应该在生产系统中运行这样的代码。虽然此代码在一个线程中运行,但任何其他线程也可能会收到OutOfMemoryException,完全扭转最初调用此代码的意图。 - Steffen Heil
1
@Steffen Heil:...并且它可能会一直运行,因为垃圾回收不需要始终收集所有对象(而且在这里优化数组创建很容易)。此外,垃圾回收与终结不同,终结器不需要及时运行或根本不需要运行。将该单个挂起对象的终结推迟到循环结束是完全合理的行为。这导致循环永远不会结束... - Holger
我非常乐意听到更好的解决方案(不包括对垃圾回收的提示)。 - Agnius Vasiliauskas
显示剩余2条评论

-4
如果您正在使用JUnit和Spring,请尝试在每个测试类中添加以下内容:
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)

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