Memory management in JAVA
Tags: java, beginners, interview, corejava
Published: 2023-10-06 11:47:43


Picture yourself with a bottle of water that you consume from throughout the day. It is inevitable that unless you replenish the bottle, there will come a point in the day when it will be completely devoid of water. This concept also applies to memory and anything else that has a limited lifespan.

JVM designates memory whenever we create new variables, objects, call a method. If we mindlessly just keep on using memory without freeing it, we are bound to encounter a java.lang.OutOfMemoryError exception. Usually, this error is raised when there is not enough space in the Java heap to allocate an object. It can also occur when there is a lack of native memory to support class loading. The solution - Memory management

**Memory management** refers to the process of assigning resources for new objects & removing unused objects to free up space for new allocations. In Java, memory management is handled automatically, eliminating the need for us to implement intricate logic in our application code.

Understanding the key features of Java like platform independence, object life cycle, concurrency, security, libraries, memory management etc. allow us to maximize its offerings and also write clean and efficient code. Additionally, as JVM is the foundation for other Java-based programming languages, acquiring knowledge about Java internals assists in working with those programming languages.

Having set the context, let's understand various blocks of memory in Java. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o9t5duzj6tcl0p98p07k.png)

**Stack Memory** is responsible for static memory allocation & executing threads. Whenever a new method is called, a new block is added on top of the stack, containing specific values for that method. After the method finishes executing, its corresponding stack frame is cleared & the program returns to the calling method.

Advantages :
- Each thread has its own stack area in stack memory, ensuring thread safety.
- Memory allocation and deallocation processes are faster.
- Accessing stack memory is quicker.

Disadvantages :
- Stack memory is fixed and cannot be resized once created.
- It follows a Last-In-First-Out (LIFO) approach, making random access impossible.
- Lacks scalability and flexibility.

**Heap** in Java is a shared chunk of memory that is created when the JVM starts up. It can be fixed or variable & does not need to be contiguous. It is used for dynamically allocating memory for Java objects and JRE classes during the execution of a Java program & is divided in three generations: young, old & permanent generation.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n3d1ho2zfbp633m5i3ge.png)

_Young generation_ is where newly created objects are allocated. It consists of three sub-parts: _Eden, Survivor1, and Survivor2_. Objects are initially allocated in Eden. When Eden becomes full, a minor garbage collection occurs and the live objects are moved to Survivor1, and then to Survivor2. Therefore, we can say that Survivor1 and Survivor2 hold objects that survived the minor garbage collection.

Objects allocated in the young generation are assigned an age, and when that age is reached, they are moved to the _old generation_. Typically, long-surviving objects are stored in the old generation. A major garbage collection is performed on the old generation to collect dead objects.

The _permanent generation_ is used by the JVM to store metadata about classes and methods, including the Java standard libraries. This space is cleaned during a full garbage collection.

Advantages :
- It is not fixed in size and can grow and shrink as needed. 
- Allows for random access.

Disadvantages :
- It is shared across threads therefore not thread-safe.
- Accessing heap memory is slower compared to other.
- Process of allocating and deallocating memory in the heap is more complex compared to stack memory.

Having understood the java memory model let's now delve into the concept of **garbage collection (gc)**, the feature that enables java to manage memory automatically and efficiently. The aim of gc is to find unused objects and then eliminate or delete them in order to free up memory. An object is considered eligible for Garbage Collection under the following conditions:
1. It is not being used by any program or thread.
2. It has no static references or the references are null.
3. The object is created within a block and once the control exits that block, the reference goes out of scope.

GC is controlled by a thread known as the Garbage Collector. Java provides two methods, System.gc() and Runtime.gc(), for sending requests to the JVM for garbage collection. It is important to remember that this is merely a request and it is not guaranteed that garbage collection will occur. When the garbage collector removes an object from memory, it first calls the finalize() method of that object before removing it.

Garbage collection in Java employs a mark-and-sweep algorithm.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p9q5vpo9p9x6fea80tbw.png)

When a Java object is created in the heap, it initially has a mark bit set to 0 (false). During the mark phase, the garbage collector traverses object trees starting from their roots. If an object is reachable from the root, its mark bit is set to 1 (true). Objects that are unreachable will have their mark bits remain unchanged. During the sweep phase, the garbage collector scans the heap, reclaiming memory from all items with a mark bit of 0 (false).