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

问题描述 投票:198回答:22

是否可以在Java中强制进行垃圾收集,即使这样做很棘手?我知道System.gc();Runtime.gc();,但他们只建议做GC。我怎么强迫GC?

java garbage-collection
22个回答
155
投票

您最好的选择是调用System.gc(),这只是对垃圾收集器的一个提示,您希望它能够进行收集。由于垃圾收集器是非确定性的,因此无法强制立即收集。


6
投票

JVM规范没有说明垃圾收集的具体内容。因此,供应商可以自由地实施GC。

因此,这种模糊性导致垃圾收集行为的不确定性。您应该检查您的JVM详细信息,以了解垃圾收集方法/算法。此外,还有自定义行为的选项。


4
投票

如果你需要强制垃圾收集,也许你应该考虑如何管理资源。您是否正在创建内存中持久存在的大对象?您是否正在创建具有Disposable接口的大型对象(例如图形类),并且在完成时不调用dispose()?你是否只在一个方法中只需要一个类级别的声明?


3
投票

如果你想描述你需要垃圾收集的原因会更好。如果您使用的是SWT,则可以使用ImageFont等资源来释放内存。例如:

Image img = new Image(Display.getDefault(), 16, 16);
img.dispose();

还有一些工具可以确定不受干扰的资源。


0
投票

如果你的内存不足并获得OutOfMemoryException,你可以尝试通过java -Xms128m -Xmx512m而不是java开始编程来增加java可用的堆空间量。这将使您的初始堆大小为128Mb,最大为512Mb,远远超过标准的32Mb / 128Mb。


0
投票

另一个选择是不创建新对象。

对象池可以减少Java中对GC的需求。

对象池通常不会比创建对象(特别是轻量级对象)更快,但它比垃圾收集更快。如果创建了10,000个对象,则每个对象为16个字节。这是GC必须回收的160,000字节。另一方面,如果您不需要同时使用所有10,000个,则可以创建一个池来回收/重用对象,这样就不需要构建新对象,也不需要GC旧对象。

像这样(未经测试)。如果您希望它是线程安全的,您可以将LinkedList替换为ConcurrentLinkedQueue。

public abstract class Pool<T> {
    private int mApproximateSize;
    private LinkedList<T> mPool = new LinkedList<>();

    public Pool(int approximateSize) {
        mApproximateSize = approximateSize;
    }

    public T attain() {
        T item = mPool.poll();
        if (item == null) {
            item = newInstance();
        }
        return item;
    }

    public void release(T item) {
        int approxSize = mPool.size(); // not guaranteed accurate
        if (approxSize < mApproximateSize) {
            recycle(item);
            mPool.add(item);
        } else if (approxSize > mApproximateSize) {
            decommission(mPool.poll());
        }
    }

    public abstract T newInstance();

    public abstract void recycle(T item);

    public void decommission(T item) { }

}

0
投票

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


0
投票

真的,我不懂你。但要清楚“无限对象创建”我的意思是我的大系统中有一些代码会创建处理内存并在内存中存活的对象,我实际上无法获得这段代码,只是手势!

这是正确的,只有手势。几张海报已经提供了很多标准答案。让我们一个接一个:

  1. 我实际上无法获得这段代码

正确的,没有实际的jvm - 这只是一个规范,一堆描述所需行为的计算机科学......我最近开始从本机代码初始化Java对象。为了得到你想要的东西,唯一的办法就是做所谓的积极归零。如果做错了错误是如此糟糕,以至于我们必须将自己局限于问题的原始范围:

  1. 我的大系统中的一些代码会创建对象

这里的大多数海报都假设你说你正在使用界面,如果这样我们将不得不看你是一次交给整个对象还是一个项目。

如果您不再需要对象,则可以为对象分配null,但如果出错则会生成空指针异常。我敢打赌,如果你使用NIO,你可以做得更好

任何时候你或我或任何其他人得到:“我需要那么可怕。”它几乎是普遍的前驱,几乎完全破坏了你正在尝试的工作....给我们写一个小样本代码,从中消毒任何实际使用的代码并向我们展示你的问题。

不要感到沮丧。这通常解决的是你的dba正在使用在某处购买的软件包,并且原始设计没有针对海量数据结构进行调整。

这很常见。


0
投票

您可以尝试使用Runtime.getRuntime().gc()或使用实用方法System.gc()注意:这些方法不能确保GC。它们的范围应限于JVM,而不是在应用程序中以编程方式处理它。


-1
投票

FYI

方法调用System.runFinalizersOnExit(true)保证在Java关闭之前调用终结器方法。但是,此方法本质上是不安全的,已被弃用。另一种方法是使用方法Runtime.addShutdownHook添加“shutdown hooks”。

Masarrat Siddiqui


-1
投票

有一些间接的方式来强制垃圾收集器。您只需要使用临时对象填充堆,直到执行垃圾收集器为止。我用这种方式强制垃圾收集器的类:

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。


51
投票

jlibs library has a good utility class for garbage collection。您可以使用WeakReference对象使用一个漂亮的小技巧强制垃圾收集。

来自jlibs的RuntimeUtil.gc()

   /**
    * This method guarantees that garbage collection is
    * done unlike <code>{@link System#gc()}</code>
    */
   public static void gc() {
     Object obj = new Object();
     WeakReference ref = new WeakReference<Object>(obj);
     obj = null;
     while(ref.get() != null) {
       System.gc();
     }
   }

-1
投票

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

由于垃圾收集器是虚拟机,我们不能强迫它在那里进行清理。而是我们将请求与垃圾收集器排队。它取决于虚拟机,在特定时间之后(这可能会在系统之间发生变化,通常在分配给JVM的阈值内存已满时),实际机器将释放空间。 :d


-1
投票

以下代码取自assertGC(...)方法。它试图强制收集不确定性的垃圾收集器。

   List<byte[]> alloc = new ArrayList<byte[]>();
   int size = 100000;
   for (int i = 0; i < 50; i++) {
        if (ref.get() == null) {
             // Test succeeded! Week referenced object has been cleared by gc.
             return;
        }
        try {
             System.gc();
        } catch (OutOfMemoryError error) {
             // OK
        }
        try {
             System.runFinalization();
        } catch (OutOfMemoryError error) {
             // OK
        }

        // Approach the jvm maximal allocatable memory threshold
        try {
             // Allocates memory.
             alloc.add(new byte[size]);

             // The amount of allocated memory is increased for the next iteration.
             size = (int)(((double)size) * 1.3);
        } catch (OutOfMemoryError error) {
             // The amount of allocated memory is decreased for the next iteration.
             size = size / 2;
        }

        try {
             if (i % 3 == 0) Thread.sleep(321);
        } catch (InterruptedException t) {
             // ignore
        }
   }

   // Test failed! 

   // Free resources required for testing
   alloc = null;

   // Try to find out who holds the reference.
   String str = null;
   try {
        str = findRefsFromRoot(ref.get(), rootsHint);
   } catch (Exception e) {
        throw new AssertionFailedErrorException(e);
   } catch (OutOfMemoryError err) {
        // OK
   }
   fail(text + ":\n" + str);

来源(为了清楚起见,我添加了一些评论):NbTestCase Example


-1
投票

如果您正在使用JUnit和Spring,请尝试在每个测试类中添加它:

@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)

42
投票

强制GC的最佳方法(如果不是唯一的方法)是编写自定义JVM。我相信垃圾收集器是可插拔的,所以你可以选择一个可用的实现并调整它。

注意:这不是一个简单的答案。


38
投票

使用Java™ Virtual Machine Tool Interface (JVM TI),功能

jvmtiError ForceGarbageCollection(jvmtiEnv* env)

将“强制VM执行垃圾收集”。 JVM TI是JavaTM Platform Debugger Architecture (JPDA)的一部分。


25
投票

是的,几乎可以强制你必须以相同的顺序调用方法,同时这些方法是:

System.gc ();
System.runFinalization ();

即使只是一个对象来清理这两个方法的使用同时强制垃圾收集器使用finalise()方法的无法访问的对象释放分配的内存并执行finalize()方法所声明的内容。

然而,使用垃圾收集器是一种可怕的做法,因为使用它可能会给软件带来过载甚至可能比内存更糟糕,垃圾收集器有自己的线程,这是不可能控制加上取决于gc使用的算法可能需要更多的时间并且考虑效率非常低,你应该在gc的帮助下检查你的软件是否最糟糕,因为它肯定是坏的,一个好的解决方案不能依赖于gc。

注意:只是要记住这只有在finalize方法不是对象的重新分配时才有效,如果发生这种情况,对象将保持活着,它将具有技术上可能的复活。


20
投票

OutOfMemoryError的文档中,它声明除非VM在完全垃圾收集后未能回收内存,否则不会抛出它。因此,如果您继续分配内存直到出现错误,那么您将已经强制进行完整的垃圾回收。

据推测,你真正想问的问题是“我怎么能回收我认为我应该通过垃圾收集来回收的内存?”


15
投票

要手动请求GC(而不是从System.gc()):

  1. 转至:JDK中的bin文件夹eg.-C:\ Program Files \ Java \ jdk1.6.0_31 \ bin
  2. 打开jconsole.exe
  3. 连接到所需的本地进程。
  4. 转至内存选项卡,然后单击执行GC。

11
投票

.gc是未来版本中的淘汰候选者 - 一位太阳工程师曾评论说,世界上可能只有不到二十人真正知道如何使用.gc() - 我昨晚在中心/关键时刻做了几个小时的工作数据结构使用SecureRandom生成的数据,在某个地方只有超过40,000个对象,vm会慢下来,好像它已经用尽指针一样。显然,它正在扼杀16位指针表并展示经典的“失败的机械”行为。

我尝试了-Xms等等,一直都在努力,直到它运行到大约57,xxx的东西。然后它会在gc()之后运行从57,127到57,128的gc - 大约在Easy Money营地的代码膨胀速度。

您的设计需要基本的重新工作,可能是滑动窗口方法。


9
投票

您可以从命令行触发GC。这对batch / crontab非常有用:

jdk1.7.0/bin/jcmd <pid> GC.run

见:

© www.soinside.com 2019 - 2024. All rights reserved.