Crash course on JVM Memory Management:

JVM provides automatic memory management. 

In languages like C, developers manage memory explicitly using functions like malloc() and free().

JVM makes this process easier and takes the burden away from the developers. (which of course has its own pros and cons).

Garbage collector is a key subsystem within the JVM that is responsible for allocating memory for new objects (read more later), maintaining references to active objects and reclaiming memory occupied by unreachabe (garbage) objects. 

JVM memory is divided into two broad classes:

- Young Generation: Smaller spaces where new obects are allocated. Garbage collected frequently (more on this later)
- Old Generation: Larger space for objects that have survived multiple GC cycles. Grows slowly as compared to the young generation.

GC (Garbage collection) are of two types:
- Minor - Targets young generation and runs more frequently.
- Major - Targets both young and old generation, runs less frequently. 

Why have these generations?

- Hypothesis - Most objects die young, and there are fewer references from older to younger objects. 

In simple languages, your applications generally create more objects that are short lived and less objects that live longer. So dividing these two types into different types enables running GC (minor) more frequently, scanning smaller memory area. Overall making it pretty efficient. 

FYI: If you want to learn JVM internals in good depth you can join The GeekNarrator membership and get access to the ongoing series along with other member-only videos.Other memory spaces: 

- Permanent generation: Pre JDK-8 used to have this permgen space for storing internal representations for classes, metadata, static vars, and interned strings. 

- Metaspace : JDK 8+ got rid of Permgen and replaced it with Metaspace. Class metadata is stored in native memory with its size limited using `MaxMetaspaceSize` JVM option.

- Compressed class space: when `UseCompressedClassPointers` is enabled class metadata is stored in a separate area, allowing 64-bit class pointers to be represented with 32-bit offsets. why? memory optimisation and improved cache efficiency. 

- Code cache: A native memory area used to store compiled code generated by JIT compilers, managed by Code-Cache sweeper.

Types of Garbage collectors:
- G1 Garbage collector: Default since Java 9, Designed for large heaps (>4GB), balances throughput and latency, suitable for most modern applications. 

- Z Garbage collector: Designed for very large heaps, focuses on low latency with pause times under 10ms, Concurrent, single generation, region-based and NUMA-aware. 

- Shenandoah Collector: Aims for consistently low pause times, regardless of heap size, performs most work concurrently, including compaction. Suitable for apps requiring responsiveness and short pauses. 

- Parallel Collector:  Focuses on maximising throughput, uses multiple threads for both minor and major collections. Suitable for batch processing  and applications that can tolerate some pauses.

- Serial Collector: Single threaded, good for small heaps and single processor machines, useful for non-interactive background applications. 

- CMS Collector (Deprecated since Java 9 and removed now) 

Important Options:
`-XX:+UseSerialGC` : Enables Serial and Serial Old collectors.

`-XX:+UseParNewGC` : Enables ParNew and Serial Old collectors.

`-XX:+UseConcMarkSweepGC` : Enables ParNew and CMS collectors, with Serial Old as a fallback.

`-XX:+UseParallelGC` : Enables Parallel Scavenge and Parallel Old collectors.

`-XX:+UseG1GC` : Enables the G1 collector for both generations.

If you want to learn JVM internals in good depth you can join The GeekNarrator membership and get access to the ongoing series along with other member-only videos.