Java Garbage Collector - When Does It Collect

Java garbage collector - When does it collect?

It runs when it determines that it is time to run. A common strategy in generational garbage collectors is to run the collector when an allocation of generation-0 memory fails. That is, every time you allocate a small block of memory (big blocks are typically placed directly into "older" generations), the system checks whether there's enough free space in the gen-0 heap, and if there isn't, it runs the GC to free up space for the allocation to succeed.
Old data is then moved to the gen-1 heap, and when space runs out there, the GC runs a collection on that, upgrading the data which has been there longest to the gen-2 heap, and so on. So the GC doesn't just "run". It might run on the gen-0 heap only (and most collections will do just that), or it might check every generation if it really has to free up a lot of memory (which is only necessary fairly rarely).

But this is far from the only strategy. A concurrent GC runs in the background, cleaning up while the program is running. Some GC's might run as part of every memory allocation. An incremental collector might do that, scanning a few objects at every memory allocation.

The entire point in a garbage collector is that it should just do its thing without requiring any input from the user. So in general, you can't, and shouldn't, predict when it'll run.

I believe Suns JVM gained a generational GC not too long ago (v1.6 maybe? I haven't coded Java for ages, so not sure on this, but I remember being surprised not too long ago, when one of the selling points for the new version was "a generational GC". Not least because .NET has had one since day 1.)

Other JVM's are of course free to pick whichever strategy they like.

EDIT: The above part about Java and generational GC is not true. See below for more details:

The 1.0 and 1.1 Virtual Machines used a mark-sweep collector, which could fragment the heap after a garbage collection. Starting with Java 1.2, the Virtual Machines switched to a generational collector, which has a much better defragmentation behavior (see Java theory and practice: Garbage collection and performance).

So Java actually has a generational GC for ages. What's new in Java 6 is the Garbage-First garbage collector (G1) that is available in Java 6u14. According to the article claiming the release in 1.6.0_14: It is not enabled by default. The parallel collector is still the default GC and is the most efficient GC for common household usage. G1 is meant to be an alternative for the concurrent collector. It is designed to be more predictable and enable fast allocation with memory regions design.

When does GC starts collecting garbage?

The short answer: the garbage collector will never collect space that is currently being used. So in your example, as long as keywords is in scope, whatever it references won't be collected. But when you assign it to null, the objects that it used to reference may be eligible for collection, if no one else is referencing them. That doesn't mean those objects will be collected, just that it might happen.

The long answer:
First, you have to understand that there are multiple implementations of the Java Virtual Machine. There is no requirement in the Java 8 Virtual Machine Specification that a JVM even do garbage collection.

That said, garbage collection is a common feature in JVMs. The Java Language Specification 8 states, in Chapter 1:

The Java programming language ... includes automatic storage
management, typically using a garbage collector, to avoid the safety
problems of explicit deallocation (as in C's free or C++'s delete).

The JavaDoc for Java 8 documents the System.gc() method as follows:

Runs the garbage collector.

Calling the gc method suggests that the Java Virtual Machine expend
effort toward recycling unused objects in order to make the memory
they currently occupy available for quick reuse. When control returns
from the method call, the Java Virtual Machine has made a best effort
to reclaim space from all discarded objects.

It then goes on to say that System.gc() is equivalent to Runtime.getRuntime().gc(). The JavaDoc for that method says:

Runs the garbage collector. Calling this method suggests that the Java
virtual machine expend effort toward recycling unused objects in order
to make the memory they currently occupy available for quick reuse.
When control returns from the method call, the virtual machine has
made its best effort to recycle all discarded objects.

The name gc stands for "garbage collector". The virtual machine
performs this recycling process automatically as needed, in a separate
thread, even if the gc method is not invoked explicitly.

So, here are the takeaways:

  1. Garbage collection isn't required by the spec
  2. It's a common part of JVM's anyway
  3. You don't get any real control over it, you can only "suggest" that the JVM do some garbage collection
  4. Garbage collection often happens in its own thread, whenever the JVM wants it to happen, so it's totally unpredictable

How garbage collection determine time of garbage objects to collect and how long object is pushed from young generation to old generation?

Think of the young generation like a stack. Objects are allocated at the current stack pointer and the pointer is moved by the size of the object that has been allocated (this is really quick).

When the pointer reaches the top of the Eden space memory range a minor GC needs to occur. To track all live objects, the collector will start by creating a root set. This is a list of all the objects directly accessible from your program. The collector does this by scanning the registers and the program stack looking for object references. Each of these objects will be marked as live and then scanned for object references that they contain for the variables you define in them. This happens recursively until all objects that can be accessed from your code are marked.

Once this is complete, any live objects in the Eden space are copied to the empty (to) survivor space. Also at this time, any objects that are still live in the occupied (from) survivor space will be copied into the to survivor space, unless they have reached the tenuring threshold, in which case they are promoted (copied) into the old generation.

To answer your question more specifically, the time an object spends in the young generation will depend on several factors because of the algorithm explained above.

  1. How big the Eden space is (the bigger it is the longer it will take to fill up)
  2. How fast you are instantiating new objects (the faster you do this, the faster the Eden space will fill up).
  3. The tenuring threshold. This is how many times an object will be copied between the survivor spaces before being promoted to the old generation.
  4. The size of the survivor spaces will also play a part as objects will be promoted more quickly if these fill up

Collection of the old generation can use one of several different algorithms but determining which objects are still live uses the same approach as for the young generation, i.e. create a root set and then scan recursively all object references.

Garbage Collector doesn't immediately collect finished thread

Could it be that you are asking the wrong question? What I mean: is the fact that garbage collection is not happening "immediately" a problem for you?

I am pretty sure - when you start another such thread that needs a lot of memory, the GC will kick in all by itself.

If that "delay" is actually a problem for you, you might consider doing the "very deep dive" in order to understand how GC actually works for your version of the JVM; to then start using the many many command line options that exist to fine-tune GC behavior to your needs.

But if a "delay" in freeing up memory (to other Java objects) is not an issue; then don't start fixing it.

When and how are classes garbage collected in Java?

A class in Java can be garbage-collected when nothing references it. In most simple setups this never happens, but there are situations where it can occur.

There are many ways to make a class reachable and thus prevent it from being eligible for GC:

  • objects of that class are still reachable.
  • the Class object representing the class is still reachable
  • the ClassLoader that loaded the class is still reachable
  • other classes loaded by the ClassLoader are still reachable

When none of those are true, then the ClassLoader and all classes it loaded are eligible for GC.

Here's a constructed example (full of bad practices!) that should demonstrate the behaviour:

Create a bytecode file GCTester.class in a directory (not package!) x. It's source code is:

public class GCTester {
public static final GCTester INSTANCE=new GCTester();

private GCTester() {
System.out.println(this + " created");
}

public void finalize() {
System.out.println(this + " finalized");
}
}

Then create a class TestMe in the parent directory of x:

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.lang.reflect.Field;

public class TestMe {
public static void main(String[] args) throws Exception {
System.out.println("in main");
testGetObject();
System.out.println("Second gc() call (in main)");
System.gc();
Thread.sleep(1000);
System.out.println("End of main");
}

public static void testGetObject() throws Exception {
System.out.println("Creating ClassLoader");
ClassLoader cl = new URLClassLoader(new URL[] {new File("./x").toURI().toURL()});
System.out.println("Loading Class");
Class<?> clazz = cl.loadClass("GCTester");

System.out.println("Getting static field");
Field field = clazz.getField("INSTANCE");

System.out.println("Reading static value");
Object object = field.get(null);
System.out.println("Got value: " + object);

System.out.println("First gc() call");
System.gc();
Thread.sleep(1000);
}
}

Running TestMe will produce this (or similar) output:


in main
Creating ClassLoader
Loading Class
Getting static field
Reading static value
GCTester@1feed786 created
Got value: GCTester@1feed786
First gc() call
Second gc() call (in main)
GCTester@1feed786 finalized
End of main

In the second to last line we see that the GCTester instance is finalized, which can only mean that the class (and ClassLoader) are eligible for garbage collection.

When does Java's garbage collection free a memory allocation?

When you set the reference of any object to null, it becomes available for garbage collection. It still occupies the memory until the garbage collector actually runs. There are no guarantees regarding when GC will run except that it will definitely run and reclaim memory from unreachable objects before an OutOfMemoryException is thrown.

You can call System.gc() to request garbage collection, however, that's what it is - a request. It is upto GC's discretion to run.

Using a WeakReference can help in some cases. See this article by Brian Goetz.

Does Java garbage collector collect even if a reference out of scope is further referenced?

your variable arraylist is static, so doesn't go out of scope. It therefore maintains references to its element until the end of the program.

They can't be garbage collected, so you're not "just lucky".

Where & when does Garbage Collector starts its role?

The answer is never. x is a local variable of a primitive type. Its value is allocated on the stack. Once the stack frame is popped (ex. method returns), the memory is de-allocated immediately, no garbage collection involved for it.

As for the object referenced by myObj, if no reference chain exists to it after the method returns, it will be eligible for garbage collection.



Related Topics



Leave a reply



Submit