How to force garbage collection in Java?

Viewed 392822

Is it possible to force garbage collection in Java, even if it is tricky to do? I know about System.gc(); and Runtime.gc(); but they only suggest to do GC. How can I force GC?

25 Answers

How to Force Java GC

Okay, here are a few different ways to force Java GC.

  1. Click JConsole's Perform GC button
  2. Use JMap's jmap -histo:live 7544 command where 7544 is the pid
  3. Call the Java Diagnostic Console's jcmd 7544 GC.run command
  4. Call System.gc(); in your code
  5. Call Runtime.getRuntime().gc(); in your code

enter image description here

None of these work

Here's the dirty little secret. None of these are guaranteed to work. You really can't force Java GC.

The Java garbage collection algos are non-deterministic, and while all of these methods can motivate the JVM to do GC, you can't actually force it. If the JVM has too much going on and a stop-the-world operation is not possible, these commands will either error out, or they will run but GC won't actually happen.

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

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

Fix the darn problem

If you've got a memory leak or object allocation problem, then fix it. Sitting around with your finger on Java Mission Control's Force Java GC button only kicks the can down the road. Profile your app with Java Flight Recorder, view the results in VisualVM or JMC, and fix the problem. Trying to force Java GC is a fools game.

enter image description here

You can try using Runtime.getRuntime().gc() or use utility method System.gc() Note: These methods do not ensure GC. And their scope should be limited to JVM rather than programmatically handling it in your application.

Another options is to not create new objects.

Object pooling is away to reduce the need GC in Java.

Object pooling is generally not going to be faster than Object creation (esp for lightweight objects) but it is faster than Garbage Collection. If you created 10,000 objects and each object was 16 bytes. That's 160,000 bytes GC has to reclaim. On the other hand, if you don't need all 10,000 at the same time, you can create a pool to recycle/reuse the objects which eliminates the need to construct new objects and eliminates the need to GC old objects.

Something like this (untested). And if you want it to be thread safe you can swap out the LinkedList for a 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) { }

}

We can trigger jmap -histo:live <pid> using the java runtime. This will force a full GC on heap to mark all the live objects.

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());
}

I did some experimentation (see https://github.com/mikenakis/ForcingTheJvmToGarbageCollect) trying about a dozen different ways of performing a garbage collection, including ways described in this answer, and more, and I found that there is absolutely no frigging way to deterministically force the JVM to do a complete garbage collection. Even the best answers to this question are only partially successful in that the best they achieve is some garbage collection, but never a guaranteed full garbage collection.

My experimentation has showed that the following code snippet yields the best (least bad) results:

public static void ForceGarbageCollection()
{
    long freeMemory = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
    for( ; ; )
    {
        Runtime.getRuntime().gc();
        Runtime.getRuntime().runFinalization();
        long newFreeMemory = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
        if( newFreeMemory == freeMemory )
            break;
        freeMemory = newFreeMemory;
        sleep( 10 );
    }
}

Where the sleep() function is as follows:

private static void sleep( int milliseconds )
{
    try
    {
        Thread.sleep( milliseconds );
    }
    catch( InterruptedException e )
    {
        throw new RuntimeException( e );
    }
}

Unfortunately, that number 10 in that sleep( 10 ) is magic; it assumes that you are doing a moderate number of memory allocations per second, which incur a moderate amount of finalization. If you are going through objects faster, then 10 might be inadequate and you may need to wait longer. You could set it to 100 to be sure, but no matter what you set it to, there will always be a chance that it will not be enough.

That having been said, in a controlled environment where that 10 is enough, this approach has been observed to consistently eliminate all unreachable objects from memory, while no other approach mentioned in this Q&A does. The experiment code I linked to on github proves so.

In my opinion, the fact that the Java Virtual Machine provides no means of performing a forced-on-demand, unconditional, deterministic, absolutely thorough, stop-the-world garbage collection makes it BROKEN.

To put it in a different way, the creators of the JVM are so full of hubris as to think that they know better than us whether we want to do that or whether we should want to do that. Don't be so arrogant. If something works as if by magic, then some means of bypassing the magic must be provided.

I wanted to force gc, because my code was frozen for a long time when it happened. The aim is to smooth the charge, by regularly cause gc. The solutions listed doesnt forced anything in my environment.

So:

  • I request the memory for temporary variable,
  • simply, by increments,
  • and monitor the memory and stop the operation as soon as gc is triggered.

It works easily but you have to tune.

Runtime rt = Runtime.getRuntime();
double usedMB = (rt.totalMemory() - rt.freeMemory()) / 1024 / 1024;

if (usedMB > 1000) // only when necessary
{
byte[][] for_nothing = new byte[10][];

for (int k = 0; k < 10 ; k ++)
    for_nothing[k] = new byte[100_000_000];
}

System.gc();
Runtime.getRuntime().gc();
Runtime.getRuntime().runFinalization(); 

On OracleJDK 10 with G1 GC, a single call to System.gc() will cause GC to clean up the Old Collection. I am not sure if GC runs immediately. However, GC will not clean up the Young Collection even if System.gc() is called many times in a loop. To get GC to clean up the Young Collection, you must allocate in a loop (e.g. new byte[1024]) without calling System.gc(). Calling System.gc() for some reason prevents GC from cleaning up the Young Collection.

If you are using JUnit and Spring, try adding this in every test class:

@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
Related