Chapter 24 Advanced 55 Questions

Practice Questions — Java Memory Model and Garbage Collection

← Back to Notes
8 Easy
9 Medium
6 Hard

Topic-Specific Questions

Question 1
Easy
What is the output?
int x = 10;
int y = x;
y = 20;
System.out.println(x);
System.out.println(y);
Primitives on the stack store values, not references.
10
20
Question 2
Easy
What is the output?
int[] a = {1, 2, 3};
int[] b = a;
b[0] = 99;
System.out.println(a[0]);
Arrays are objects on the heap. 'b = a' copies the reference, not the array.
99
Question 3
Easy
What is the output?
String s1 = "Hello";
String s2 = s1;
s2 = "World";
System.out.println(s1);
System.out.println(s2);
Strings are immutable. Reassigning s2 creates a new String object.
Hello
World
Question 4
Easy
How many objects are eligible for GC after the marked line?
String a = new String("X");
String b = new String("Y");
String c = new String("Z");
a = b;
b = null;
// <-- How many objects eligible for GC here?
Count objects that have no references pointing to them.
1 (the original object referenced by a, which held "X")
Question 5
Easy
What is the output?
Runtime rt = Runtime.getRuntime();
System.out.println(rt.maxMemory() > 0);
System.out.println(rt.availableProcessors() > 0);
These are basic runtime queries that always have positive values.
true
true
Question 6
Medium
How many objects are eligible for GC after the marked line?
Object a = new Object();  // obj1
Object b = new Object();  // obj2
Object c = a;              // c -> obj1
a = b;                     // a -> obj2
b = null;                  // b -> null
c = null;                  // c -> null
// <-- How many eligible?
Trace each reference step by step.
1 (obj1)
Question 7
Medium
What error occurs and why?
static void recurse() {
    recurse();
}

public static void main(String[] args) {
    recurse();
}
Each method call creates a new stack frame.
StackOverflowError
Question 8
Medium
What is the output?
import java.lang.ref.WeakReference;

String strong = new String("Data");
WeakReference<String> weak = new WeakReference<>(strong);
System.out.println(weak.get());
strong = null;
System.gc();
System.out.println(weak.get());
After strong is null, the only reference to "Data" is weak.
Data
null
Question 9
Medium
What is the difference between StackOverflowError and OutOfMemoryError?
Each relates to a different memory area.
StackOverflowError occurs when the stack runs out of space, typically due to infinite or very deep recursion. Each thread has its own stack (~512 KB). OutOfMemoryError occurs when the heap runs out of space and GC cannot reclaim enough memory. The heap is shared across all threads and is much larger.
Question 10
Medium
What are GC roots? Why are they important?
GC roots are the starting points for reachability analysis.
GC roots are the starting points from which the garbage collector traces object reachability. They include: (1) local variables and parameters on thread stacks, (2) static variables, (3) active thread objects, (4) JNI references. Any object reachable from a GC root (directly or through a chain of references) is considered alive. Objects not reachable from any GC root are garbage.
Question 11
Hard
Explain generational garbage collection. Why is it more efficient than collecting the entire heap?
Most objects die young (weak generational hypothesis).
Generational GC divides the heap into Young Generation (Eden + Survivor spaces) and Old Generation. New objects go to Eden. When Eden fills, Minor GC runs -- it is fast because the young generation is small and most objects are already dead. Surviving objects move to Survivor spaces, then eventually to Old Generation. Old Generation is collected less frequently (Major GC), which is slower. This is efficient because of the weak generational hypothesis: most objects die young, so collecting the young generation frequently reclaims most garbage with minimal effort.
Question 12
Hard
What is an 'island of isolation' in garbage collection? Give an example.
Think about circular references where no external reference exists.
An island of isolation occurs when a group of objects reference each other (circular references) but no external GC root can reach any of them. All objects in the group are eligible for GC despite having non-null references, because the reachability from GC roots is what matters, not the reference count. Example: Object A references B, B references A, but both original references (from local variables) are set to null. The A-B pair forms an isolated island that GC will collect.
Question 13
Hard
Name three common causes of memory leaks in Java and how to prevent each.
Think about static collections, unclosed resources, and inner classes.
(1) Static collections that grow without bound: objects added to a static List/Map are never collected. Fix: use bounded caches (LRU), clear collections, or use WeakHashMap. (2) Unclosed resources: streams, connections, and readers hold native handles. Fix: use try-with-resources. (3) Anonymous/inner classes holding implicit references to outer class: the inner class keeps the outer instance alive. Fix: use static inner classes or lambda expressions (which do not capture 'this' unless needed).

Mixed & Application Questions

Question 1
Easy
What is the output?
String s = new String("Hello");
String t = s;
System.out.println(s == t);
== compares references for objects.
true
Question 2
Easy
What is the output?
String s = new String("Hello");
String t = new String("Hello");
System.out.println(s == t);
System.out.println(s.equals(t));
Each 'new' creates a different object on the heap.
false
true
Question 3
Medium
How many objects are eligible for GC at the marked line?
class Node {
    Node next;
    Node(Node next) { this.next = next; }
}

Node a = new Node(null);  // node1
Node b = new Node(a);     // node2 -> node1
Node c = new Node(b);     // node3 -> node2 -> node1
a = null;
b = null;
// <-- How many eligible here?
Trace the reference chain from c.
0
Question 4
Medium
What happens when this code runs?
java.util.List<int[]> list = new java.util.ArrayList<>();
while (true) {
    list.add(new int[1_000_000]);
}
The list keeps growing and objects are never removed.
OutOfMemoryError: Java heap space
Question 5
Medium
What is the output?
void method() {
    int x = 10;
    int y = 20;
    String s = new String("Hello");
}
// After method() returns, how many items are removed from the stack?
Each local variable occupies a slot in the method's stack frame.
The entire stack frame (containing x, y, and s) is popped. 3 local variables are removed from the stack. The String object on the heap becomes GC eligible.
Question 6
Hard
How many objects are GC eligible at the marked line?
class Node { Node next; }

Node a = new Node(); // n1
Node b = new Node(); // n2
a.next = b;
b.next = a;
a = null;
b = null;
// <-- How many eligible?
This is an island of isolation. No external reference can reach either node.
2 (both n1 and n2)
Question 7
Hard
What is the output?
import java.lang.ref.SoftReference;

SoftReference<String> soft = new SoftReference<>(new String("Cache"));
System.out.println(soft.get());
System.gc();
System.out.println(soft.get());
Soft references are only collected when memory is low.
Cache
Cache (most likely)
Question 8
Easy
Where are static variables stored in JVM memory?
Think about class-level data.
Static variables are stored in the Method Area (Metaspace in Java 8+). Since Metaspace uses native memory (not heap), static variables are not subject to heap GC. They live for the entire lifetime of the class, which is typically the entire program execution.
Question 9
Medium
What is the difference between -Xms and -Xmx JVM flags?
One is initial, one is maximum.
-Xms sets the initial heap size (the heap starts at this size). -Xmx sets the maximum heap size (the heap cannot grow beyond this). For example, -Xms256m -Xmx1024m means the heap starts at 256 MB and can grow up to 1024 MB. Setting both to the same value avoids heap resize overhead in production.
Question 10
Hard
Why is finalize() deprecated? What should you use instead?
Think about reliability and performance.
finalize() is deprecated (Java 9+) because: (1) No guarantee it will ever run. (2) No guarantee of timing -- it may run long after the object is GC eligible. (3) It delays GC by one cycle (the object must survive an extra GC to be finalized). (4) Performance overhead from the finalization queue. Use AutoCloseable with try-with-resources for deterministic cleanup. For cleanup of non-resource objects, use java.lang.ref.Cleaner (Java 9+).

Multiple Choice Questions

MCQ 1
Where are objects created with 'new' stored in JVM memory?
  • A. Stack
  • B. Heap
  • C. Method Area
  • D. Program Counter
Answer: B
B is correct. All objects created with new are allocated on the heap. The stack stores only local variables (primitives and references to heap objects).
MCQ 2
What happens when the stack runs out of space?
  • A. OutOfMemoryError
  • B. StackOverflowError
  • C. NullPointerException
  • D. The JVM resizes the stack
Answer: B
B is correct. StackOverflowError occurs when the call stack exceeds its limit, typically due to infinite recursion. The stack has a fixed size per thread.
MCQ 3
What does garbage collection do?
  • A. Deletes unused source code files
  • B. Automatically reclaims memory from unreachable objects
  • C. Compresses files on disk
  • D. Removes unused import statements
Answer: B
B is correct. Garbage collection identifies objects on the heap that are no longer reachable from any GC root and reclaims their memory automatically.
MCQ 4
What makes an object eligible for garbage collection?
  • A. Calling delete on the object
  • B. Setting the reference to 0
  • C. When no live references point to it
  • D. When the object is more than 5 minutes old
Answer: C
C is correct. An object becomes GC eligible when no live reference chain from any GC root can reach it. Java does not have a delete operator.
MCQ 5
Where are static variables stored?
  • A. Stack
  • B. Heap
  • C. Method Area (Metaspace)
  • D. Native Method Stack
Answer: C
C is correct. Static variables are stored in the Method Area (Metaspace since Java 8). They belong to the class, not to instances.
MCQ 6
What does -Xmx512m mean?
  • A. Set stack size to 512 MB
  • B. Set maximum heap size to 512 MB
  • C. Set metaspace to 512 MB
  • D. Set minimum heap size to 512 MB
Answer: B
B is correct. -Xmx sets the maximum heap size. -Xms sets the initial/minimum heap size. -Xss sets the stack size per thread.
MCQ 7
What is the difference between Young Generation and Old Generation?
  • A. Young stores classes, Old stores objects
  • B. Young stores new objects; Old stores long-lived objects that survived multiple GC cycles
  • C. Young uses more memory than Old
  • D. There is no difference
Answer: B
B is correct. New objects go to Young Generation (Eden). Objects that survive multiple Minor GC cycles are promoted to Old Generation (Tenured). This exploits the observation that most objects die young.
MCQ 8
What does System.gc() do?
  • A. Forces immediate garbage collection
  • B. Requests garbage collection (JVM may ignore it)
  • C. Deletes all objects on the heap
  • D. Crashes the JVM
Answer: B
B is correct. System.gc() is a request, not a command. The JVM is free to ignore it. Never rely on it for correctness in production code.
MCQ 9
Which reference type is collected only when memory is low?
  • A. Strong
  • B. Weak
  • C. Soft
  • D. Phantom
Answer: C
C is correct. Soft references are kept as long as memory is sufficient. They are collected only when the JVM needs memory, making them ideal for caches.
MCQ 10
Why is finalize() deprecated?
  • A. It runs too frequently
  • B. It is unreliable: no guarantee of execution timing, delays GC, performance overhead
  • C. It was replaced by delete()
  • D. It causes compilation errors
Answer: B
B is correct. finalize() may never run, has unpredictable timing, delays GC by one cycle, and adds overhead. Use try-with-resources (AutoCloseable) instead.
MCQ 11
What is an 'island of isolation' in GC?
  • A. An object with no fields
  • B. A group of objects with circular references but no external references from GC roots
  • C. A single isolated thread
  • D. An object stored in static memory
Answer: B
B is correct. When objects reference each other in a cycle but no GC root can reach any of them, they form an island of isolation. All are GC eligible despite having non-null references.
MCQ 12
Which of these is NOT a GC root?
  • A. Local variables on thread stacks
  • B. Static variables
  • C. Objects in the young generation
  • D. Active thread objects
Answer: C
C is correct. Objects in the young generation are not GC roots -- they are the objects BEING evaluated for collection. GC roots are references FROM stacks, static fields, and active threads TO objects.
MCQ 13
What happens when both a try block and close() throw exceptions in try-with-resources, from a memory perspective?
  • A. The close exception is lost, causing a resource leak
  • B. Both exceptions are preserved (suppressed exception mechanism), and the resource is properly cleaned up
  • C. The JVM crashes
  • D. The try exception is lost
Answer: B
B is correct. try-with-resources guarantees that close() is called. If both throw exceptions, the close() exception is added as a suppressed exception. The resource is properly released, preventing memory/resource leaks.
MCQ 14
What replaced PermGen in Java 8?
  • A. Heap
  • B. Stack
  • C. Metaspace
  • D. Native Memory Pool
Answer: C
C is correct. Java 8 replaced PermGen (Permanent Generation, part of the heap) with Metaspace, which uses native memory. This eliminated the common 'java.lang.OutOfMemoryError: PermGen space' error because Metaspace can grow dynamically.
MCQ 15
Each thread in Java has its own:
  • A. Heap
  • B. Stack
  • C. Method Area
  • D. String Pool
Answer: B
B is correct. Each thread has its own stack (and PC register). The heap, method area, and string pool are shared across all threads.

Coding Challenges

Challenge 1: Object Counter

Easy
Create a class that tracks how many instances have been created using a static counter. Also track how many instances are still alive by decrementing the counter when an object becomes GC eligible (hint: use a static counter and demonstrate with nullification + System.gc()).
Sample Input
(No input required)
Sample Output
Created: 3 After nullifying 2: alive references = 1 After GC: creation count still = 3 (static counter does not decrease)
Use static variables. Demonstrate the difference between creation count and live reference count.
public class ObjectCounter {
    private static int creationCount = 0;
    private String name;

    ObjectCounter(String name) {
        this.name = name;
        creationCount++;
        System.out.println("Created: " + name + " (total: " + creationCount + ")");
    }

    public static void main(String[] args) {
        ObjectCounter a = new ObjectCounter("A");
        ObjectCounter b = new ObjectCounter("B");
        ObjectCounter c = new ObjectCounter("C");
        System.out.println("Creation count: " + creationCount);

        a = null;
        b = null;
        System.gc();
        System.out.println("After nullifying A and B:");
        System.out.println("Creation count (static): " + creationCount);
        System.out.println("Only C is still reachable: " + c.name);
    }
}

Challenge 2: GC Eligibility Analyzer

Medium
Write a program that creates several objects with various reference patterns (null, reassignment, scope exit, circular). For each case, print how many objects are GC eligible and explain why.
Sample Input
(No input required)
Sample Output
Case 1 (null): 1 object eligible Case 2 (reassign): 1 object eligible Case 3 (scope): 1 object eligible Case 4 (circular): 2 objects eligible
Cover all four eligibility cases. Use comments to explain.
public class GCAnalyzer {
    static class Node { Node next; String name;
        Node(String n) { name = n; }
    }

    public static void main(String[] args) {
        // Case 1: Reference set to null
        Node n1 = new Node("A");
        n1 = null; // A eligible
        System.out.println("Case 1 (null): 1 eligible");

        // Case 2: Reference reassigned
        Node n2 = new Node("B");
        n2 = new Node("C"); // B eligible, C alive
        System.out.println("Case 2 (reassign): 1 eligible (B)");

        // Case 3: Scope exit
        createAndReturn();
        System.out.println("Case 3 (scope): 1 eligible (D)");

        // Case 4: Circular with no external reference
        Node e = new Node("E");
        Node f = new Node("F");
        e.next = f;
        f.next = e;
        e = null;
        f = null; // E-F island of isolation
        System.out.println("Case 4 (circular): 2 eligible (E, F)");

        System.gc();
    }

    static void createAndReturn() {
        Node d = new Node("D");
    }
}

Challenge 3: Memory Monitor

Medium
Write a program that monitors heap memory usage before and after allocating a large number of objects, then after GC. Display used/free/total memory in MB at each step.
Sample Input
(No input required)
Sample Output
Step 1: Used=1MB, Free=14MB, Total=15MB Step 2 (after alloc): Used=25MB, Free=15MB, Total=40MB Step 3 (after GC): Used=1MB, Free=14MB, Total=15MB
Use Runtime.getRuntime() methods. Create an array of objects and then null it before GC.
public class MemoryMonitor {
    static void printMem(String label) {
        Runtime rt = Runtime.getRuntime();
        long total = rt.totalMemory() / (1024 * 1024);
        long free = rt.freeMemory() / (1024 * 1024);
        long used = total - free;
        System.out.printf("%s: Used=%dMB, Free=%dMB, Total=%dMB%n", label, used, free, total);
    }

    public static void main(String[] args) {
        printMem("Initial");

        Object[] data = new Object[500_000];
        for (int i = 0; i < data.length; i++) {
            data[i] = new byte[50];
        }
        printMem("After allocation");

        data = null;
        System.gc();
        printMem("After GC");
    }
}

Challenge 4: Weak Reference Cache

Hard
Implement a simple cache using WeakReferences. The cache should store values that can be garbage collected when memory is needed. Demonstrate that cached values disappear after GC when no strong references exist.
Sample Input
(No input required)
Sample Output
Cache hit: value1 After GC (strong ref exists): value1 After GC (strong ref removed): null
Use java.lang.ref.WeakReference. Demonstrate the difference between having and not having a strong reference.
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;

public class WeakCache<K, V> {
    private final Map<K, WeakReference<V>> cache = new HashMap<>();

    public void put(K key, V value) {
        cache.put(key, new WeakReference<>(value));
    }

    public V get(K key) {
        WeakReference<V> ref = cache.get(key);
        return (ref != null) ? ref.get() : null;
    }

    public static void main(String[] args) {
        WeakCache<String, String> cache = new WeakCache<>();
        String value = new String("ImportantData");
        cache.put("key1", value);

        System.out.println("Before GC: " + cache.get("key1"));
        System.gc();
        System.out.println("After GC (strong ref alive): " + cache.get("key1"));

        value = null; // remove strong reference
        System.gc();
        System.out.println("After GC (no strong ref): " + cache.get("key1"));
    }
}

Challenge 5: Memory Leak Detector

Hard
Create a program that demonstrates a memory leak using a static collection, monitors memory growth over iterations, and then fixes the leak. Show memory usage before and after the fix.
Sample Input
(No input required)
Sample Output
Iteration 100: used=5MB Iteration 200: used=10MB (GROWING - LEAK) After fix: used=1MB (STABLE)
Use a static List to create the leak. Use Runtime to monitor memory. Fix by clearing or using bounded collection.
import java.util.ArrayList;
import java.util.List;

public class LeakDetector {
    private static final List<byte[]> leakyCache = new ArrayList<>();

    static long usedMB() {
        Runtime rt = Runtime.getRuntime();
        return (rt.totalMemory() - rt.freeMemory()) / (1024 * 1024);
    }

    public static void main(String[] args) {
        System.out.println("=== Demonstrating Memory Leak ===");
        for (int i = 1; i <= 500; i++) {
            leakyCache.add(new byte[10_000]); // ~10KB per entry
            if (i % 100 == 0) {
                System.gc();
                System.out.println("Iteration " + i + ": used=" + usedMB() + "MB, cache=" + leakyCache.size());
            }
        }

        System.out.println("\n=== Fixing the Leak ===");
        leakyCache.clear();
        System.gc();
        System.out.println("After clear: used=" + usedMB() + "MB, cache=" + leakyCache.size());
    }
}

Need to Review the Concepts?

Go back to the detailed notes for this chapter.

Read Chapter Notes

Want to learn Java with a live mentor?

Explore our Java Masterclass