How is Java different from C & C++ in terms of memory management? #


Java handles memory automatically, while C (malloc/free) and C++ (new/delete) require the programmer to allocate and release memory.

πŸ“Œ 1: Manual Memory Management in C

  • What?: The programmer allocates and deallocates memory.
  • Risk: Memory leaks occur if free() is not called.
  • Example (C: Manual Allocation and Deallocation)
    // Allocate memory
    int *ptr = (int*) malloc(sizeof(int)); 
    
    *ptr = 10;
    
    printf("Value: %d\n", *ptr);
    
    // Free memory to prevent leaks
    free(ptr); 

πŸ“Œ 2: Manual Memory Management in C++

  • What?: Uses new and delete for allocation/deallocation.
  • Risk: If delete is forgotten, memory leaks occur.
  • Example (C++: Using new/delete)
    int* ptr = new int(10); // Allocate memory
    
    std::cout << "Value: " << *ptr << std::endl;
    
    delete ptr; // Manually free memory
  • Later Versions of C++: Introduced smart pointers (std::unique_ptr, std::shared_ptr) to manage memory automatically.

πŸ“Œ 3: Automatic Garbage Collection in Java

  • What?: Java manages memory automatically, reclaiming unused objects.
  • How?: The Garbage Collector (GC) runs periodically to free up memory.
  • Example (Java: No Manual Freeing Needed)
    // Memory allocated
    Demo demo = new Demo(10);
    
    System.out.println("Demo: " + demo);
    
    // No need to manually free memory
    // Once there are no references to an object
    // GC will free the memory

πŸ“Œ 4: Summary

Feature C (Manual) C++ (Manual) Java (Automatic GC)
Allocation malloc() new() new Demo()
Deallocation free() delete Handled by Garbage Collector
Risk Memory leaks if free() is forgotten Memory leaks if delete is forgotten MOSTLY No memory leaks
Performance Fast Fast Slightly slower

What is a Memory Leak? #


πŸ“Œ Overview

  • Memory leak: A program fails to release unused memory

  • Causes increased memory usage over time

  • Can eventually lead to OutOfMemoryError and slow performance

    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        // Allocate memory
        int *ptr = (int*) malloc(sizeof(int)); 
        
        *ptr = 10;
        
        printf("Value: %d\n", *ptr);
        
        //Programmer forgot to free memory
        //free(ptr); 
        return 0;
    }

πŸ“Œ Why Are Memory Leaks Rare in Java?

  • Garbage Collection (GC): The JVM automatically reclaims unused objects, reducing leaks.
  • (Remember) Memory leaks still occur if objects are unintentionally kept in memory

πŸ“Œ Examples of Memory Leaks in Java

1: Static Variables Holding References

  • What?: Static variables persist throughout the program, preventing garbage collection.

    private static List<Something> millionObjects
                            = new ArrayList<>();
    
    //Code to Populate millionObjects with objects
    
    //Objects in millionObjects persist until end of program
    //unless you write code to remove objects from the list!
    

2: Un-closed Resources (File, Database, Network)

  • What?: Resources like file streams stay open, causing memory leaks.
    import java.io.*;
    
    public class FileLeakExample {
        public static void main(String[] args) 
                            throws IOException {
            FileInputStream file 
                    = new FileInputStream("data.txt");
            
            // No file.close() β†’ Memory leak
        }
    }
  • Fix: Use try-with-resources to auto-close resources.
    try (FileInputStream file = new FileInputStream("data.txt")) {
        // Auto-closed at end of block
    }

How does Garbage Collection work? #


πŸ“Œ What?

  • Garbage Collection: Process where Java automatically removes un-referenced objects from memory.

πŸ“Œ How?

  • If an object has no references, it is unreachable => Object eligible for Garbage Collection (GC).
  • Example:
    String str = new String("Hello");
    
    System.out.println(str);
    
    str = null;
    // Object is now unreachable β†’ Eligible for GC
    
    //Other code

πŸ“Œ When?

  • The JVM decides when to run GC based on memory usage. (There are many algorithms!)
  • You can request GC using System.gc()
    • But it is not guaranteed to run immediately

Memory Spaces in Java #


Java efficiently manages memory by using

  • Stack for method calls & local variables
  • Heap for objects
  • Metaspace for class metadata and
  • Code Cache for JIT-compiled machine code

πŸ“Œ 1: Stack Memory

  • What? Stores method calls & local variables.
  • When? Exists only during method execution.
  • Where? Each thread has its own stack.
  • Code Example
    • New stack frame is created when add(a, b) is called.
    • Stack frame is removed when add() finishes, freeing memory.
    public class StackExample {
        public static void main(String[] args) {
            int a = 10;  // Stored in Stack
            int b = 20;  // Stored in Stack
            add(a, b);   // Creates new Stack Frame
        }
    
        static int add(int x, int y) {
            // Parameters, Local variables stored in Stack
            int sum = x + y;  
            return sum;
        }
    }

πŸ“Œ 2: Heap Memory

  • What? Stores objects.
  • Why? Needed for objects where object lifespan is unknown.
  • Code Example
    • p1 and p2 references are in Stack, but objects exist in Heap.
    • Objects persist even after the method call ends, until GC removes them.
    // `p1` in Stack, object in Heap
    Person p1 = new Person("Alice"); 
    
    // Another object in Heap
    Person p2 = new Person("Bob");
    
    // A very long program follows!
    // A very long program follows!
    // A very long program follows!

πŸ“Œ 3: Metaspace (Replaces PermGen)

  • What? Stores class metadata
    • Stores the class structure (including the constant pool, field, and method data).
    • Stores method and constructor code, including static methods.
  • When? Holds data until the class is unloaded from JVM.

πŸ“Œ 4: Code Cache

  • What? Stores JIT-compiled machine code for faster execution.
  • Why? Reduces repeated bytecode interpretation, improving performance.
  • How? Managed by the JVM’s Just-In-Time (JIT) compiler.

Summary

  1. Stack β†’ Stores local variables & method calls, cleared when methods exit.
  2. Heap β†’ Stores objects, cleaned by Garbage Collector.
  3. Metaspace β†’ Holds class metadata.
  4. Code Cache β†’ Stores JIT-compiled machine code. Reduces repeated bytecode interpretation.

Comparison: Stack vs Heap Memory #


Feature Stack Heap
Stores Local variables, method calls Objects, instance variables
Lifespan Cleared after method exits Objects remain until GC removes them
Memory Management Automatic Managed by Garbage Collector
Thread Scope Each thread has its own stack Shared among all threads

Why is GC Complex? #


πŸ“Œ Steps in Garbage Collection

  • Step 1: Marking β†’ Finds objects still in use.
  • Step 2: Sweeping β†’ Removes unused objects from memory.
  • Step 3: Compacting β†’ Rearranges memory to avoid fragmentation.

πŸ“Œ Why is it Complex?

Task Complexity
Finding Unused Objects Objects may be referenced indirectly
Minimizing Application Pauses GC involves a lot of work. Doing GC without impacting running applications can be a challenge.
Avoiding Memory Fragmentation Free memory may be scattered
Providing Flexibility Different applications have different needs

Reference Counting vs Mark & Sweep #


πŸ“Œ 1: Reference Counting

What?

  • Each object has a reference counter: Tracks how many references point to it
  • When the count reaches zero, the object is eligible for garbage collection.

Why?

  • Simple and fast
  • Memory can be freed immediately when no references exist

Issue?

  • Fails with circular references β†’ Two objects referencing each other will never be collected.

Example:

  • Problem: Even though objA and objB are unreachable, their reference count is still 1, preventing GC.
  • Solution: Java does not use Reference Counting
    class A {
        B b;
    }
    
    class B {
        A a;
    }
    
    
    //Assume this code in a method
    
    A a = new A(); // objA on Heap refCount = 1
    B b = new B(); // objB on Heap refCount = 1
    
    a.b = b; // objB on Heap refCount increases to 2
    b.a = a; // objA on Heap refCount increases to 2
    
    a = null; // objA on Heap refCount decreases to 1
    b = null; // objB on Heap refCount decreases to 1
    
    // Even though objA and objB are unreachable, 
    //their refCount is still 1.
    
    //Lot of other code follows
    

πŸ“Œ 2: Mark & Sweep

What?

  • JVM scans the heap and marks objects that are still reachable.
  • Unmarked objects are deleted.

Why?

  • Avoids circular reference issues.

How?

  • STEP 1: Mark Phase – JVM starts from root references and marks all reachable objects
    • Active Thread Stack β†’ Local variables in method calls.
    • Static Variables β†’ Class-level variables (static int x).
    • JNI References β†’ Objects referenced from native C/C++ code.
  • STEP 2: Sweep Phase – JVM removes unmarked (unused) objects, reclaiming memory.

πŸ“Œ Comparison: Reference Counting vs Mark & Sweep

Feature Reference Counting Mark & Sweep
Concept Tracks the number of references to each object Scans memory and marks reachable objects
Performance Fast, immediate cleanup Slower, requires scanning of the heap
Handles Circular References? ❌ Fails βœ… Works correctly
Used in Java? ❌ No βœ… Yes

What Are Generations in Java Garbage Collection? #


πŸ“Œ Overview

  • What? Java’s heap memory is divided into generations – Young and Old.
  • Approach: Frequent garbage collection for short-lived objects (Young Generation) and less frequent collection for long-lived objects (Old Generation).
  • Why? To improve Garbage Collection (GC) efficiency.

πŸ“Œ Generations in Java

  • Young Generation: Stores new objects.
  • Old Generation: Stores long-lived objects.

πŸ“Œ Garbage Collection Cycles

  • Minor GC Cycle: Cleans Young Generation (runs frequently).
  • Major GC (Full GC) Cycle: Cleans Old Generation (runs less often).

πŸ“Œ Object Lifecycle in Generations

  1. Objects are created in the Young Generation.
  2. Minor GC cycles frequently clean up short-lived objects.
  3. Surviving objects move to the Old Generation after multiple Minor GC cycles.
  4. Major GC cycle runs when the Old Generation fills up, cleaning long-lived objects.

πŸ“Œ Why Is This Efficient?

  • Separates objects based on their likelihood of removal, optimizing GC effort.
  • Most objects are short-lived, so frequent Young Generation GC quickly reclaims memory.
  • Old Generation objects persist longer, making frequent GC on them inefficient.

πŸ“Œ NOTE

  • The Permanent Generation (PermGen) was a memory area in the Java Virtual Machine (JVM) used to store class metadata.
  • Replaced by Metaspace in Java 8, which does NOT use heap memory.

What is Stop-the-World (STW) GC? #


  • STW GC pauses all application threads for garbage collection.
  • No user code runs during this time.
  • Ensures memory safety but affects performance and response time.
  • When Does Stop-the-World GC Occur?
    • Minor GC: Brief pause for Young Generation cleanup.
    • Major GC (Full GC): Longer pause for Old Generation cleanup.
    • New GC methods: Aim to reduce or eliminate STW pauses.

Types of Garbage Collectors in Java #


πŸ“Œ 1: Serial GC

  • What? A simple, single-threaded garbage collector.
  • Why? Best for small applications with low memory requirements.
  • When? Typically used when JVM detects a single-core CPU
  • How? Performs Stop-the-World (STW) GC
  • Where? Best for small desktop applications.
  • Example: Enable Serial GC
    java -XX:+UseSerialGC MyApp

πŸ“Œ 2: Parallel GC (Throughput Collector)

  • What? Uses multiple threads for garbage collection.
  • When? Suitable for multi-threaded applications.
  • How? Runs Minor GC (Young Gen) in parallel, reducing collection time.
  • Where? Ideal for medium-sized applications with multi-core processors.
  • Example: Enable Parallel GC
    java -XX:+UseParallelGC MyApp

πŸ“Œ 3: G1 GC (Garbage First GC)

  • Why? Reduces long GC pauses by prioritizing regions with most garbage.
  • When? Best for apps needing low-latency performance.
    • Choose when response time is important
  • How? Divides heap into fixed-size regions and collects garbage concurrently.
    • More granular memory management – Instead of treating the heap as only "Young vs. Old," G1 GC manages memory in smaller, more flexible regions
    • Better efficiency – Prioritizes garbage collection based on garbage density
  • Example: Enable G1 GC
    java -XX:+UseG1GC MyApp

πŸ“Œ 4: ZGC (Z Garbage Collector)

  • What? Fully concurrent GC avoids full application freezes, improving response time.
  • Why? Best for applications needing near real-time performance.
  • When? If response time is a high priority and the heap size is large (multi-terabyte scale)
  • How? Performs almost all GC work concurrently, avoiding long pauses.
  • Example: Enable ZGC
    java -XX:+UseZGC MyApp

πŸ“Œ Comparison : Java Garbage Collectors

GC Type Best For Pause Time Threads Introduced In
Serial GC Small applications High (Stop-the-World) Single-threaded Java 1.0
Parallel GC Medium-sized apps Medium Multi-threaded Java 1.3
G1 GC Large apps needing low latency Low Multi-threaded Java 7
ZGC Large-scale, real-time apps Very Low Multi-threaded Java 11

πŸ“Œ Key Difference in One Line

  • Serial GC: "I'll stop everything and clean up memory in one go"
  • Parallel GC: "I'll use multiple threads to clean up faster, but I still need to pause everything a number of times"
  • G1 GC: "I'll clean up memory little by little, but I still need to stop you a few times."
  • ZGC: "I'll clean up memory silently in the background, and you won't even notice!"

Customization Options for Garbage Collection #


Option Description Example
-XX:+UseSerialGC Enables Serial GC (single-threaded, best for small apps) java -XX:+UseSerialGC MyApp
-XX:+UseParallelGC Enables Parallel GC (multi-threaded, good for high throughput) java -XX:+UseParallelGC MyApp
-XX:+UseG1GC Enables G1 GC (low-latency, region-based GC) java -XX:+UseG1GC MyApp
-XX:+UseZGC Enables ZGC (fully concurrent, ultra-low pause time) java -XX:+UseZGC MyApp
-Xms<size> Sets initial heap size java -Xms512m MyApp
-Xmx<size> Sets maximum heap size java -Xmx2g MyApp
-XX:+PrintGCDetails Prints detailed GC logs java -XX:+PrintGCDetails MyApp

Why is finalize() Method Deprecated in Java 9? #


πŸ“Œ What?

  • finalize(): A method called before an object is garbage collected to perform cleanup tasks
  • Why Deprecated?: Unreliable, slow, and can cause security risks

πŸ“Œ Problems with finalize():

  1. Unpredictable Execution – The JVM does not guarantee when or if finalize() will run.
  2. Performance Overhead – Objects with finalize() require extra GC cycles, delaying memory cleanup.
  3. Security Risks – Bad code can cause security risks.
    • For example: Objects can be revived inside finalize(), preventing garbage collection.
    • If an object assigns this to a static field or another reachable reference inside finalize(), it becomes reachable again and won’t be garbage collected.

Java Uses Call by Value #


Java is always "Call by Value" – for primitives, it passes values, and for objects, it passes reference copies

πŸ“Œ Call by Value

  • What? A copy of the value is passed to the method.
  • The original variable remains unchanged after the method call.
  • Example:
    public class CallByValueExample {
        static void modify(int num) {
            num = num + 10;
        }
    
        public static void main(String[] args) {
            int x = 20;
            modify(x);
            System.out.println(x); // Output: 20 (unchanged)
        }
    }

πŸ“Œ Passing Objects in Java: Also Call by Value

  • What? When an object is passed, the reference is copied, not the actual object.
  • Why? The reference itself cannot be changed, but the object's internal values can be modified.
  • Example:
    class Person {
        String name;
    }
    
    public class CallByReferenceExample {
        static void modify(Person p) {
            p.name = "Alice"; // Modifies the object
            
            //p = new Person(); 
            //Does 
        }
    
        public static void main(String[] args) {
            Person person = new Person();
            person.name = "Bob";
            modify(person);
            System.out.println(person.name); 
            // Output: Alice (object modified)
        }
    }