Java JVM Garbage Collection Interview Questions
Reviewed by Mark Dickie · Last updated
12 questions on this page you can answer and see marked on the spot. Go to the first one
JVM garbage collection is the process by which the Java Virtual Machine automatically frees memory held by objects the application can no longer reach. For interviews, you should know the generational heap model, where new objects allocate in the young generation and survivors promote to the old generation, with metaspace holding class metadata. Be ready to explain stop-the-world pauses and why generational assumptions keep minor GCs cheap. You should also know when to read GC logs and switch collectors.
How does generational garbage collection work?
The heap is split into generations on the assumption that most objects die young. New objects allocate in the young generation; survivors of a few minor collections are promoted to the old generation. Minor GCs scan only the young generation and are frequent but cheap. Full GCs scan the entire heap and are expensive. You want to avoid them when possible.
| Collector | Pause type | Best for |
|---|---|---|
| Serial | Stop-the-world, single-threaded | Small heaps, single-core machines |
| Parallel | Stop-the-world, multi-threaded | Throughput on multi-core |
| G1 | Region-based, mostly concurrent | Large heaps with pause targets |
| ZGC | Concurrent, low-latency | Very large heaps needing low latency |
| Shenandoah | Concurrent, compacting | Low pause on large heaps |
Which garbage collector should I choose?
The answer depends on your latency and throughput requirements:
- Identify your pause-time budget from your SLA.
- Measure current pause frequency and duration with GC logging or JFR.
- Start with the JVM default for your JDK version (G1 for JDK 9 and later).
- If pauses exceed your budget, test ZGC or Shenandoah with large heaps.
- Only tune flags like
-XX:MaxGCPauseMillisafter measuring; blind tuning often makes things worse.
Key facts
- Tarmac has 29 Java interview questions on this topic, 25 of them on this page, at difficulty 1–5 of 5.
- Tarmac tracked 1,645 job postings asking for Java in September 2026.
- Roles asking for Java advertise a median base salary of £80,000, across 352 job postings as of September 2026.
- Tarmac last reviewed these Java interview questions on 14 October 2026.
At a glance
| Questions | 25 shown · 29 in the bank |
|---|---|
| Difficulty | 1–5 of 5 |
| Formats | True / false, Short answer, Multiple choice, Fill in the blank, Code output, Find the bug |
What you'll review
- jvm
- garbage collection
Practice questions
Try one before you open the answer. Pick an option and press Check; it's marked on the spot.
True or false: In Java, local variables declared inside a method are automatically initialized to their type's default value (for example, int to 0 and boolean to false).#
Options
Show answer
False. Java does not give local variables default values. Only instance and static fields are automatically set to defaults such as 0 or false. A local variable must be definitely assigned before it is read, or the compiler rejects the code.
This is false. Only instance fields and static fields receive default values, assigned by the JVM during object or class initialization. Local variables live in the method's stack frame and are not initialized. The compiler enforces definite assignment, so reading a local variable before it has been assigned is a compile-time error rather than a silent zero or false.
Which JVM runtime data area is used to allocate memory for all class instances (objects) created with the new keyword?#
Show answer
The heap. Objects and arrays created with new are allocated on the JVM heap, which is shared by all threads and is the region managed by the garbage collector.
The JVM heap is the runtime data area from which memory for all class instances and arrays is allocated. The JVM stack stores frames holding local variables and partial results, the method area stores class metadata, and the native method stack serves native (JNI) calls. The heap is shared among all threads and is the target of garbage collection, which is why it is the correct answer.
Java source code is compiled into bytecode (.class files) rather than machine code specific to a particular operating system or CPU architecture. This bytecode is interpreted and/or JIT-compiled by the JVM at runtime, which means the same compiled .class file can run on any platform that has a compatible JVM implementation.#
Options
Show answer
True. Java source code is compiled into platform-neutral bytecode (.class files) by javac. The JVM — which is itself platform-specific — interprets and JIT-compiles that bytecode at runtime, so the same compiled class file can run on any operating system with a compatible JVM implementation.
The Java compiler (javac) translates .java source files into platform-neutral bytecode stored in .class files. The JVM — which IS platform-specific — loads and executes that bytecode, so the same compiled artifact runs unchanged on Windows, Linux, macOS, or any other OS with a JVM. This is the core of Java's 'write once, run anywhere' model. The statement is true.
In the Java Virtual Machine memory model, which runtime data area is shared among ALL threads of a running Java application?#
Options
Show answer
The Heap is the JVM runtime data area shared among all threads. It stores all object instances and arrays, and every thread in the application reads from and writes to the same heap. The JVM Stack, Program Counter Register, and Native Method Stack, by contrast, are each allocated per-thread and are private to a single thread.
The JVM Heap is a single shared memory area where all object instances and arrays are allocated. Every thread in the application accesses the same heap, which is why heap objects can be passed between threads and why thread-safety mechanisms (synchronization, volatile, etc.) are needed. By contrast, the JVM Stack, the Program Counter Register, and the Native Method Stack are all created on a per-thread basis — each thread has its own private set of these areas. Therefore the Heap is the correct answer.
True or False: Java source code is compiled into platform-independent bytecode, which is then executed by the JVM.#
Options
Show answer
True. The javac compiler converts Java source into platform-independent bytecode stored in .class files, and the JVM loads and executes that bytecode on whichever platform it is installed on. This compile-once, run-anywhere model is the core design principle of Java.
The javac compiler translates .java source files into .class files containing JVM bytecode. This bytecode is platform-independent; it is the JVM's job to load, verify, and execute (interpret or JIT-compile) it on the host machine.
Fill in the blank: Java source code is compiled into _____ code, which the JVM then loads, verifies, and either interprets or compiles to native machine code at runtime.#
Show answer
Fill in the blank: Java source code is compiled into bytecode code, which the JVM then loads, verifies, and either interprets or compiles to native machine code at runtime.
The javac compiler does not produce machine code directly; it emits JVM bytecode (stored in .class files). The JVM's class loader and bytecode verifier process this bytecode, and the execution engine either interprets it or the JIT compiler compiles hot paths to native instructions.
In the Java Virtual Machine, which component is responsible for automatically reclaiming memory from objects that are no longer referenced by the running program?#
Options
Show answer
The Garbage Collector is the JVM component that automatically reclaims heap memory from objects no longer reachable from any live reference. The Class Loader, JIT Compiler, and Execution Engine do not perform this reclamation; they load classes, optimize bytecode, and execute it.
The Garbage Collector periodically identifies objects that are no longer reachable from any live reference and reclaims the heap memory they occupied. The Class Loader only loads class files, the JIT Compiler optimizes bytecode into native code, and the Execution Engine interprets bytecode; none of these manage reclamation of unused objects.
In Java, the garbage collector automatically identifies unreachable objects and reclaims their heap memory without the programmer needing to explicitly free each object. True or false?#
Options
Show answer
True. Java's garbage collector automatically identifies unreachable objects and reclaims their heap memory, so programmers do not free each object by hand. It traces reachability from GC roots, which removes the need for manual deallocation calls like C's free() or C++'s delete.
This is true. The JVM's garbage collector traces object reachability from GC roots and reclaims heap memory held by objects that cannot be reached. The programmer does not call a deallocation function for each object, unlike C's free() or C++'s delete. The timing of collection is up to the JVM, but the reclamation of unreachable objects is automatic.
What is the primary purpose of garbage collection in the JVM?#
Show answer
The primary purpose is to automatically reclaim heap memory occupied by objects that are no longer reachable from any live reference, so the programmer does not have to deallocate memory manually. This helps avoid leaks caused by forgotten objects, although objects that are still referenced are never collected.
The JVM garbage collector finds objects that cannot be reached from any live reference (such as local variables, static fields, or active threads) and reclaims the heap memory they occupy. This frees the programmer from explicit deallocation and helps prevent leaks of forgotten objects. It does not collect objects that are still reachable, so a leak can still occur if references are kept unintentionally.
In Java, the programmer must explicitly deallocate memory for objects that are no longer needed, just as in C you call free() or in C++ you call delete.#
Options
Show answer
Java does not require explicit deallocation, so the statement is false. The JVM's garbage collector automatically reclaims heap objects that are no longer reachable, whereas C requires free() and C++ requires delete for manual memory release.
The statement is false. Java provides automatic garbage collection: the JVM's garbage collector traces object reachability and reclaims heap memory from objects that are no longer reachable. The programmer does not explicitly free individual objects, and there is no Java equivalent of calling free() or delete. Manual deallocation is the C/C++ model that Java replaces.
In the Java Virtual Machine specification (as implemented by HotSpot), which of the following runtime data areas is shared among all threads rather than allocated per thread?#
Options
Show answer
The heap is the runtime data area shared among all threads in the Java Virtual Machine. It stores class instances and arrays, which is why objects reachable from multiple threads must be synchronized. The JVM stacks, the program counter register, and native method stacks are all allocated per thread.
The heap is the runtime data area shared by all threads, where class instances and arrays are allocated. Each thread gets its own Java Virtual Machine stack, its own program counter register, and its own native method stack, which are created with the thread and destroyed when it terminates. Since only the heap is shared among the offered areas, it is the single correct answer.
What is the exact output of the following Java program when run with default JVM settings (no overridden Integer cache bounds)? Print each line exactly as it appears.#
public class Main {
public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
System.out.println(a == b);
Integer c = 128;
Integer d = 128;
System.out.println(c == d);
}
}Show answer
true
false
Autoboxing an int uses Integer.valueOf, which returns cached objects for values from -128 to 127 by default. Both 127 values therefore refer to the same cached Integer object, so the reference comparison a == b is true. The value 128 is outside the default cache range, so each autoboxing call creates a new Integer object; c == d compares two distinct references and is false. The program prints true on the first line and false on the second.
In Java, the garbage collector reclaims an object's memory only when the object is no longer _____ from any live thread or GC root. Fill in the single standard word for the blank.#
Show answer
In Java, the garbage collector reclaims an object's memory only when the object is no longer reachable from any live thread or GC root. Fill in the single standard word for the blank.
The Java garbage collector traces from a set of GC roots (active thread stacks, static fields, local variables, etc.) and marks every object transitively reachable from those roots. Any object not found during this tracing phase is considered unreachable and is eligible for collection. Because the sentence reads 'the object is no longer ___ from any live thread or GC root', the standard term that fills the blank is 'reachable', giving 'the object is no longer reachable'.
Consider the following Java code. Assume no other references exist beyond what is shown, and Node is a class with a Node next field and no other fields.#
Options
Show answer
Both X and Y are eligible for garbage collection.
After b = null, object Y is still strongly reachable through a.next, and X is still reachable through the local variable a. After a = null, no local variable or other GC root references X, so X is unreachable. Y's only strong reference came from X's next field, and since X is itself unreachable, nothing live reaches Y either. An object is eligible for collection when no chain of strong references from a GC root leads to it, so both X and Y are eligible. The option claiming X and Y keep each other alive is wrong because the references only run one way, from X to Y, and no reference points back from Y to X.
In Java, calling System.gc() guarantees that the JVM will perform a full garbage-collection cycle and reclaim all currently-unreachable objects before the method returns. True or false?#
Options
Show answer
False. System.gc() is only a hint to the JVM that garbage collection is desired. The JVM may ignore it or perform a partial collection, so unreachable objects are not guaranteed to be reclaimed before the method returns.
System.gc() is specified in the Javadoc as a hint that the programmer would like garbage collection to run. The JVM is not required to honour it: it may skip the collection, or run a partial collection that leaves some unreachable objects in place. Nothing guarantees that a particular object is reclaimed or that the call blocks until collection finishes. Some JVMs can also disable explicit GC calls with a flag such as -XX:+DisableExplicitGC. The statement is therefore false.
What does the following Java program print? Consider how the JVM handles String literals, compile-time constant concatenation, and the new String(...) constructor with respect to == versus .equals(). Give the exact output, one line per printed value.#
public class Main {
public static void main(String[] args) {
final String a = "a";
final String b = "b";
String s = "ab";
String t = a + b;
String u = new String("ab");
String v = new String(a) + new String(b);
System.out.println(s == t);
System.out.println(s == u);
System.out.println(s == v);
System.out.println(s.equals(v));
}
}Show answer
true
false
false
true
Because a and b are final String variables initialized with literals, a + b is a compile-time constant expression, so the compiler folds it to the literal "ab", which is interned in the string pool; s == t is therefore true. The new String("ab") call allocates a new heap object distinct from the pooled literal, so s == u is false. new String(a) + new String(b) is a runtime concatenation that produces another new heap object, so s == v is false. Finally, s.equals(v) compares character content, which is ab in both, so it prints true. The four lines therefore read true, false, false, true.
The class below implements double-checked locking for lazy singleton initialization. It compiles cleanly, but another thread can observe a partially constructed Singleton. Count lines from 1 (line 1 is public class Singleton {) and give the number of the buggy line.#
public class Singleton {
private static Singleton instance;
private int value;
private Singleton() { value = 42; }
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}Show answer
The bug is on line 2.
Line 2 declares instance without volatile. Under the Java Memory Model (JLS §17.4), a plain write to instance on line 9 may become visible to other threads before the writes to value performed in the constructor on line 4, because the JIT and CPU may reorder these stores. A second thread can then pass the unsynchronized null-check on line 6, skip the lock, and return an object whose value is still 0. The fix is to declare the field volatile on line 2: a volatile write happens-before any subsequent volatile read of the same field, so the constructor's writes are visible before the reference is published. The lock on line 7 alone does not fix this, because the outer null-check on line 6 reads instance without holding the lock.
Which statement about the G1 (Garbage-First) garbage collector in a modern HotSpot JVM is correct?#
Options
Show answer
G1 divides the heap into fixed-size regions and, during a pause, evacuates live objects from the regions with the most garbage first. This garbage-first selection bounds pause times without compacting the whole heap on every collection. G1 also keeps per-region remembered sets, and mixed collections evacuate only selected old regions.
G1 partitions the heap into fixed-size regions and chooses the collection set by garbage density, evacuating live objects from the regions with the most garbage first; this is the origin of the name 'Garbage-First' and is what bounds pause times. The statement about full-heap compaction on every young pause is wrong because young pauses only evacuate young regions. The claim that G1 has no remembered sets is wrong because G1 keeps a remembered set per region to track cross-region references. The claim that mixed collections evacuate the whole old generation is wrong because they evacuate only selected old regions.
Consider the following Java code, written inside a method body:#
Show answer
2 objects. After a = null and b = null, neither object is reachable from any GC root (local variables, static fields, and similar). The JVM uses reachability-from-roots tracing rather than reference counting, so the cycle A→B→A does not keep either object alive; both are unreachable and therefore eligible for garbage collection.
The JVM's garbage collectors trace reachability starting from GC roots such as local variables on the stack, static fields, and JNI references. After both local variables a and b are set to null, no root holds a reference to object A or object B, so neither is reachable. The reference cycle between them is only a path between two unreachable objects and cannot make either one reachable from a root. Because the JVM does not use reference counting, the cycle is irrelevant, and both objects are eligible for collection, giving an answer of 2.
In the HotSpot JVM (Java 17+), explain what a safepoint is, why the JVM requires it, and describe the key constraints on how and when threads reach one.#
Show answer
A safepoint is a program location at which the JVM guarantees every application thread is in a known, stable state: stack maps are available, registers are spilled, and no object reference is in an intermediate state. The JVM requires safepoints because operations such as garbage collection (and deoptimization) need a globally consistent view of the heap so they can walk the object graph, enumerate roots (thread stacks, registers, globals), and relocate or free objects. Threads must be paused at these well-defined points. In JIT-compiled code, HotSpot inserts safepoint poll checks only at specific locations, such as method returns and loop back-edges, so a thread running compiled code reaches a safepoint only at one of those polls, not at an arbitrary instruction. A thread that is running Java code must poll and block, while a thread that is blocked in native code or otherwise already in a safe state can be treated as being at a safepoint. Stop-the-world collections (young, mixed, or full) require a safepoint to pause all threads. Even ZGC, which performs most of its work concurrently, still has brief stop-the-world pauses (such as mark start and relocate start) that depend on safepoints.
A safepoint is a point where the JVM can assume every application thread is in a known, stable state, so it can safely inspect or change the heap. The garbage collector and other VM operations need this consistent snapshot to traverse live objects, enumerate roots, and relocate or free memory. In JIT-compiled code, safepoint polls are placed only at specific spots such as method returns and loop back-edges, so a thread in compiled code stops only at those polls rather than at any instruction. Stop-the-world collections require all threads to reach a safepoint, and even ZGC needs brief safepoint pauses for its mark start and relocate start phases despite doing most work concurrently.
In Java 8, HotSpot replaced the Permanent Generation (PermGen) with Metaspace for storing class metadata. Describe where Metaspace is allocated compared with PermGen, and what problem this change mainly addresses. Be specific about the memory region.#
Show answer
Metaspace is allocated in native (off-heap) memory obtained from the operating system, while PermGen was a fixed-size region inside the Java heap. Because Metaspace is not bounded by a pre-set heap partition, it can grow dynamically, limited only by -XX:MaxMetaspaceSize (unlimited by default). This removes the OutOfMemoryError: PermGen space failures that occurred when frameworks or runtime code generators (such as Spring AOP proxies, Groovy scripts, or bytecode-manipulation libraries) loaded many classes beyond a fixed PermGen size.
PermGen lived in a fixed-size region inside the Java heap, so the number of classes an application could hold was capped by a size set at startup. Metaspace stores class metadata in native memory outside the Java heap, obtained from the operating system, and grows on demand up to -XX:MaxMetaspaceSize, which is unlimited by default. The change therefore mainly targets the OutOfMemoryError: PermGen space failures seen when frameworks generated many classes at runtime.
In the G1 garbage collector (HotSpot JVM), which mechanism allows G1 to evacuate a collection set of regions without scanning the entire heap for incoming references?#
Show answer
G1 maintains a per-region Remembered Set (RSet) for each heap region. Each RSet records which other regions contain references pointing into that region, and it is populated by a write barrier that marks dirty cards, which are then refined into the RSets. When G1 evacuates a collection set, it scans only the RSets of the regions in the collection set plus the external roots, so it never has to scan the whole heap for incoming references.
G1 maintains a Remembered Set (RSet) per region that records which other regions contain references into that region. The write barrier and card tables track reference writes, and the RSets are refined concurrently and during pauses. When G1 evacuates a collection set, it scans only the RSets of those regions plus external roots, never the whole heap. A global mark bitmap tracks liveness, not inter-region references, and a reference queue is for java.lang.ref processing. A flat card table without per-region tracking would not let G1 avoid scanning all dirty cards across the entire heap, which is why per-region RSets are the key mechanism.
In JDK 21's Z Garbage Collector (ZGC), what are colored pointers for, and how do they let ZGC relocate objects concurrently while application threads keep running?#
Show answer
ZGC uses colored pointers, which store GC metadata in otherwise unused high-order bits of 64-bit object references. The metadata records states such as Marked0, Marked1, Remapped, and Finalizable. A load barrier runs whenever a reference is loaded from the heap and inspects the pointer's color. If the pointer is stale because its object has moved, the barrier remaps it to the new address and can heal the reference in place. Because relocation and reference fixing are handled lazily by this barrier, mutator threads can keep running during relocation, and stop-the-world pauses stay very short, typically around a millisecond or less.
Colored pointers reserve high-order bits of each 64-bit reference to encode GC state (Marked0, Marked1, Remapped, Finalizable), which lets the collector track object state without separate side tables on the object header. The load barrier runs on reference loads and checks these bits; when it finds a pointer to an object that has been relocated, it remaps the pointer to the new address (self-healing). Since fixing stale references happens on access rather than in a stop-the-world phase, relocation can proceed concurrently with application threads, keeping pauses very short.
Consider a HotSpot JVM (Java 17 LTS) running with default tiered compilation (C1 and C2 enabled). During profiling, a virtual call site dispatching through the interface MyInterface always received objects of type ConcreteA, the only implementor of MyInterface loaded at that time. C2 speculatively compiled the call site as a monomorphic dispatch: a receiver-type guard followed by a direct call to ConcreteA.compute(). C2 recorded a dependency asserting that ConcreteA is the sole concrete implementor of compute(). No uncommon trap has fired at this call site yet. Later, a second class ConcreteB, also implementing MyInterface, is loaded via Class.forName(). Which option best describes the JVM's response to this class-loading event?#
Options
Show answer
When HotSpot loads a second implementor of an interface whose compiled call site assumed a single implementor, it invalidates the dependent compiled code and deoptimizes it, so execution falls back to the interpreter and the method can be recompiled with updated profile data. Loading the class does not throw an IncompatibleClassChangeError, and the existing machine code is not patched in place.
C2 compiled the call site with a monomorphic receiver guard and a direct call, and it recorded a dependency that ConcreteA is the only concrete implementor of compute(). Loading ConcreteB violates that dependency, so the JVM marks the dependent nmethod as not entrant and deoptimizes it, converting active frames back to interpreter frames. Execution continues in the interpreter, which re-profiles the site, and the method may later be recompiled with both receiver types observed. Loading a second legal implementor does not throw an IncompatibleClassChangeError, and the JVM does not rewrite the inline cache of compiled code. The option claiming the existing guard keeps the code valid is wrong because the guard is not the mechanism that protects the dependency: a ConcreteB receiver would fail the guard and hit an uncommon trap, and the dependency check is what forces invalidation.
In G1 GC's concurrent marking phase, a per-thread SATB (Snapshot-At-The-Beginning) write barrier intercepts every reference field store o.f = newRef and enqueues the previous value of o.f into a per-thread SATB queue. The marker eventually drains these queues and treats each logged old value as an additional root. What is the primary correctness guarantee this mechanism provides during concurrent marking, and what is its fundamental trade-off?#
Show answer
It guarantees that every object reachable at the start of the marking cycle will be found and marked by the end of the cycle, even if its last incoming reference is overwritten during marking, because the barrier logs each overwritten old value and the marker treats those logged values as roots. The trade-off is floating garbage: an object that becomes unreachable after its reference was logged is still marked live in this cycle and is only reclaimed in a later cycle. The barrier accepts this retention so that mutators are not stalled by a full stop-the-world re-mark.
The correct answer is that the SATB barrier guarantees any object reachable at the start of marking will be found by the end of the cycle even if its last incoming reference is overwritten during marking, at the cost of floating garbage (objects that become unreachable mid-cycle but survive until the next cycle). The barrier logs the previous value of every overwritten reference field, creating a logical snapshot of the live object graph at mark start. Without this, a mutator could overwrite the only reference to an object before the concurrent marker reached it, causing that object to be wrongly classified as unreachable and potentially reclaimed while still in use. The trade-off is that an object which becomes unreachable after its old reference was logged will still be marked live — the snapshot may contain references to objects already dead — so it survives this cycle as floating garbage and can only be reclaimed in a subsequent cycle. The mechanism does not eliminate all dead objects in a single pass; it explicitly tolerates some floating garbage to avoid stalling mutators for a full stop-the-world mark. Remembered-set (RSet) maintenance uses an entirely separate write barrier (dirty-card queues refined by concurrent refinement threads), not SATB buffers.
Related interview questions
Job market
See java salaries and hiring demand from live job postings.
The other 4 questions
This page shows 25 and marks what you pick. That's as far as a page can go. A free account opens the other 4 and keeps every answer. What you miss comes back until it's right: after a day, then at longer gaps.
Free · the whole bank · 100 marked answers per 30 days · written feedback on the paid plan