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

时间:2009-09-26 13:14:48

标签: java garbage-collection

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

23 个答案:

答案 0 :(得分:153)

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

答案 1 :(得分: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();
     }
   }

答案 2 :(得分:41)

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

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

答案 3 :(得分:38)

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

jvmtiError ForceGarbageCollection(jvmtiEnv* env)

将“强制VM执行垃圾回收”。 JVM TI是JavaTM Platform Debugger Architecture (JPDA)

的一部分

答案 4 :(得分:24)

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

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

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

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

注意:只是为了记住这只有在finalize方法不是对象的重新分配时才会起作用,如果发生这种情况,对象将保持活着状态,它将具有复活,即技术上可行。

答案 5 :(得分:19)

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

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

答案 6 :(得分:15)

手动申请GC(不是来自System.gc()):

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

答案 7 :(得分:11)

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

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

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

答案 8 :(得分:9)

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

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

见:

答案 9 :(得分:6)

如何强制 Java GC

好的,这里有几种不同的强制 Java GC 方法。

  1. 点击 JConsole 的 Perform GC 按钮
  2. 使用 JMap 的 jmap -histo:live 7544 命令,其中 7544 是 pid
  3. 调用 Java 诊断控制台的 jcmd 7544 GC.run 命令
  4. 在代码中调用 System.gc();
  5. 调用Runtime.getRuntime().gc();在您的代码中

enter image description here

这些都不起作用

这是肮脏的小秘密。这些都不能保证工作。你真的不能force Java GC

Java 垃圾收集算法是非确定性的,虽然所有这些方法都可以激励 JVM 进行 GC,但您实际上无法强制它。如果 JVM 有太多事情发生并且无法执行 stop-the-world 操作,这些命令要么会出错,要么会运行但实际上不会发生 GC。

if (input.equalsIgnoreCase("gc")) {
    System.gc();
    result = "Just some GC.";
}

if (input.equalsIgnoreCase("runtime")) {
    Runtime.getRuntime().gc();
    result = "Just some more GC.";
}

解决该死的问题

如果您遇到内存泄漏或对象分配问题,请修复它。将手指放在 Java Mission Control 的 Force Java GC 按钮上,只会让罐头走下坡路。使用 Java Flight Recorder 分析您的应用程序,在 VisualVM 或 JMC 中查看结果,并修复问题。尝试force Java GC是愚蠢的游戏。

enter image description here

答案 10 :(得分:6)

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

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

答案 11 :(得分:4)

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

答案 12 :(得分:2)

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

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

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

答案 13 :(得分:0)

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

答案 14 :(得分:0)

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

答案 15 :(得分:0)

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

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

对象池通常不会比对象创建(特别是轻量级对象)要快,但是它比垃圾回收要快。如果创建了10,000个对象,则每个对象为16个字节。必须回收160,000字节的GC。另一方面,如果您不需要同时使用所有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) { }

}

答案 16 :(得分: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。

答案 17 :(得分:0)

  

真的,我不懂你的意思。但是要   明确“无限对象创造”   我的意思是有一些   我的大系统的代码创建   处理和活着的物体   记忆,我无法得到这一块   实际上是代码,只是手势!!

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

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

    1. 我的大系统中的一些代码 创建对象
    2. 这里的大多数海报都假设你说你正在使用一个界面,如果我们这样做,我们必须看看你是否一次交给整个对象或一个项目。

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

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

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

      这很常见。

答案 18 :(得分:0)

我们可以使用 java 运行时触发 jmap -histo:live <pid>。这将强制在堆上进行完整的 GC 以标记所有活动对象。

public static void triggerFullGC() throws IOException, InterruptedException {
    String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
    Process process = Runtime.getRuntime().exec(
            String.format("jmap -histo:live %s", pid)
    );
    System.out.println("Process completed with exit code :" + process.waitFor());
}

答案 19 :(得分:-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。

答案 20 :(得分:-1)

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

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

答案 21 :(得分:-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

答案 22 :(得分:-1)

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

@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)