Memory Management और Garbage Collection
Stack memory method calls और local variables store करती है — fast, और method ख़त्म होते ही automatically clean हो जाती है।
Heap memory objects store करती है (जो "new" से बनाए जाते हैं) — ये लंबे समय तक रहती है, जब तक कोई reference use ना करे।
Garbage Collector (GC) automatically उन objects को ढूँढ कर हटा देता है जिनकी कोई reference नहीं बची — Java में manually memory free करने की ज़रूरत नहीं पड़ती।
void createObjects() {
Student s = new Student(); // heap par banta hai
} // method khatam -> s ab unreachable -> GC eligible- Stack = method calls, fast, auto-clean
- Heap = objects, GC यहाँ काम करता है
- GC = automatic memory cleanup
Stack memory method calls और local variables store करती है — fast (बस एक pointer move करना होता है push/pop के लिए), और method खत्म होते ही automatically clean हो जाती है (कोई GC की ज़रूरत नहीं)। हर thread की अपनी अलग Stack होती है।
Heap memory objects store करती है (जो "new" से बनाए जाते हैं) — यह लंबे समय तक रहती है, जब तक कोई reference use न करे। Heap सब threads के बीच shared होता है (इसलिए multithreading में heap objects के access को synchronize करना पड़ता है)।
| Stack | Heap | |
|---|---|---|
| Store करता है | Method calls, local variables, references | Objects, instance fields |
| Speed | बहुत fast | थोड़ा slow |
| Size | छोटा, fixed (StackOverflowError हो सकता है) | बड़ा, configurable (-Xmx flag से) |
| Thread-specific? | हाँ, हर thread की अपनी | नहीं, सब threads shared |
| Cleanup | Automatic (method return पर) | Garbage Collector |
JVM Heap को generations में बांटता है क्योंकि experience से पता चला है — "most objects die young" (ज़्यादातर objects थोड़ी देर में ही unused हो जाते हैं, जैसे एक loop के अंदर बनाया temporary object)। Young Generation नए objects के लिए — यहाँ GC बहुत बार चलता है, fast होता है (क्योंकि छोटा area scan करना है)।
जो objects लंबे समय तक ज़िंदा रहते हैं (कई GC cycles survive कर लेते हैं), वो Old Generation (Tenured) में promote हो जाते हैं — यहाँ GC कम बार चलता है, थोड़ा slower (बड़ा area, लेकिन rarely चलता है)।
Serial GC: single-threaded, छोटी applications/single-core machines के लिए — simple लेकिन pause times बड़े हो सकते हैं।
Parallel GC: multiple threads use करता है GC के लिए, throughput-focused (total work जल्दी खत्म करना, pause times की उतनी परवाह नहीं)।
G1 (Garbage First): modern default (Java 9+), Heap को छोटे regions में बांटता है और predictable, छोटे pause times देता है — बड़ी applications के लिए best, especially जहाँ response time matter करता है।
Automatic GC होने के बावजूद memory leaks हो सकते हैं: (1) static collections में objects add करते जाना बिना कभी remove किए (जैसे एक static List जो कभी खाली नहीं होती) — static references JVM के पूरे lifetime तक "reachable" रहते हैं।
(2) unclosed resources (files, database connections) जो GC clean नहीं करता (GC सिर्फ memory manage करता है, external resources नहीं) — इसीलिए try-with-resources ज़रूरी है। (3) listeners/callbacks जो register हुए हैं लेकिन कभी unregister नहीं हुए — इन सारे cases में objects "reachable" रह जाते हैं, इसलिए GC उन्हें हटा नहीं सकता, चाहे practically वो "unused" ही हो।
पुराने Java में finalize() method था जो object garbage collect होने से पहले चलता था cleanup के लिए — लेकिन यह unpredictable था (पता नहीं कब चलेगा, कभी चले ही न, GC का control था) इसलिए Java 9+ में deprecated हो गया और Java 18 में completely remove होने की तैयारी में है।
Modern approach: AutoCloseable interface + try-with-resources use करो, जो deterministic (guaranteed, तुरंत, predictable time पर) cleanup देता है — file/connection बंद करने जैसी चीज़ों के लिए यह ही सही तरीका है।