Have you ever noticed your Java applications slowing down or even crashing unexpectedly? Java Memory Management could be the reason behind it. But do you understand how it works?
In this article, we’ll explain how Java handles memory, how garbage collection works, and how to prevent issues like memory leaks and OutOfMemoryError. Are you sure your Java code is managing memory efficiently?
Keep reading to discover how you can improve your Java Memory Management and boost your app’s performance.
Table of Contents:
What is Java Memory Management?
Java Memory Management is the process of allocating, utilizing, and releasing memory in Java. It is managed by JVM (Java Virtual Machine). The Garbage Collector is used by the JVM to clean up unused objects and memory in the background. This helps the user not to explicitly deal with the memory management in Java.
JVM Memory Structure
Master Java Today - Accelerate Your Future
Enroll Now and Transform Your Future
Java memory structure is mainly divided into 5 main categories. Let us discuss each one of them in detail.
1. Heap Area
The heap is the runtime area where the memory for all the class objects and arrays is allocated
This area is created by the JVM when it starts. It can have both fixed and dynamic sizes.
The JVM is responsible for the size of the Heap. When the new keyword is used, the object is created of the class and is stored in the Heap, and its reference is stored in the Stack.
Only one Heap is present for the JVM running process. It is the largest storage area and hence, it is divided into the following parts.
This is where the new objects are created. It further has small parts inside it. These are:
- Eden: where objects are first made.
- Survivor spaces: Where objects go if they are needed after a cleanup.
This area is cleaned up very often.
The objects that are kept for a long time in the Young Generation are moved here.
This area is not cleaned very often.
It stores the information regarding the class of any program, like the name and method. After Java 8, this part of the memory is kept out of the heap.
Example:
Scanner sc = new Scanner(System.in);
In the above example, the Scanner object is created in the Heap because all the objects are stored in the heap area of the memory. The variable sc is referencing that object and is stored in the stack.
2. Method Area
It is used to store class structure, superclass name, interface name, and constructors. It is created when the JVM starts. This part of the heap memory is shared by all the threads.
3. JVM Stacks Area
The Stack Area is created when the Thread is created in memory. It can be fixed as well as a dynamic size. The memory here is allocated as per the thread. It contains its own value and also references to the heap objects. The memory of the Stack doesn’t have to be contiguous.
A Stack Frame is a data structure that contains the data of a thread. When a method is called, a new frame is created. And when the method finishes, that frame is removed. When a method calls another method, the present frame stops, and a new one is created for the new method.
Each frame has three parts:
- Local Variable Array holds variables like int x = 10.
- Operand Stack is used during the calculations and operations.
- Frame Data holds the return address.
4. Native Method Stack
It is used when Java runs code written in other languages, like C or C++. This happens through the Java Native Interface (JNI). It is also called as the C Stack
5. Program Counter (PC) Registers
Each thread in Java has its own memory space called the program counter register. It keeps track that which line of code of the program is currently running. For Java methods, it stores the address of the instruction that has to be executed afterward. This helps the JVM to continue if it has switched between threads. But if the method is in any other language, the PC register does not have a value.
Working of a Garbage Collector
When a Java program runs, it uses the memory to create objects inside the heap. Some of the objects are used for some time, and after some time, they become useless, meaning no part of your program references them anymore. These are called garbage.
The Garbage Collector (GC) is a part of the JVM that is responsible for freeing up memory by removing unnecessary objects. The GC handles it automatically and deletes these objects.
Let us discuss the step-by-step procedure.
Step 1: Find the Unused Objects
The GC finds the variable or objects that do not point to any other object, i.e., which are unnecessary.
Step 2: Clean Them Up
Once it finds these unnecessary objects, it removes them from memory and makes space for new objects.
Step 3: Runs in the Background
The garbage collector runs automatically in the background, i.e., you don’t have to have a separate application or window to do it
Step 4: Types of Collection
In this step, the GC finds which type of collection has to be used.
- Minor GC: Cleans the place where new objects are present.
- Major or Full GC: Cleans the place where long-lived objects are present.
Step 5: Mark and Sweep
- Mark: The GC scans all the objects that are being used.
- Sweep: It deletes the objects that are no longer needed.
Note: System.gc() and Runtime.gc() are used to explicitly clean garbage collection in Java. But overusing System.gc() can affect the performance.
Garbage Collector Types
The collector has the following types in Java.
1. Serial Garbage Collector
The Serial Garbage Collector uses a single thread to do all the cleaning work. It stops all the other threads while doing its job. This is called a “stop-the-world” pause. It is suited for small applications that run in a single-threaded environment.
2. Parallel Garbage Collector (Throughput Collector)
The Parallel Garbage Collector uses multiple threads to do the process of garbage collection. As it uses multi-threading, it still stops all the applications when performing the cleaning. It has a shorter pause time as compared to the Serial GC.
It is good for applications that need high throughput and run on multi-core processors.
3. Concurrent Mark-Sweep (CMS) Garbage Collector
The CMS garbage collector cleans the garbage value while also working on the application. It works concurrently with the app and cleans the waste objects. It uses more CPU, hence it is best for apps that need quick response times, like web servers.
4. G1 (Garbage-First) Garbage Collector
G1 GC is used by applications that have large heaps. It splits the heap into small regions and collects garbage in parts. Due to which it makes the time pauses controllable.
5. Z Garbage Collector (ZGC)
ZGC is a garbage collector that works in the background and takes a very short time, usually 10 milliseconds, even if the memory is very large, approximately 100 GB. It is good for big applications that need to run slowly without any delay.
6. Shenandoah Garbage Collector
Shenandoah GC is built for very low pause times and large heaps. It performs the garbage collection in parallel with the application, which makes the pause time short, regardless of the size of the heap.
Unlock Your Future in Java
Start Your Java Journey for Free Today
Memory Leaks and Their Prevention in Java
A memory leak occurs when the program points to memory that is no longer needed, which prevents the garbage collector from cleaning it.
This makes your application use more and more memory slowly, and can lead to slow performance issues and application crashes.
It is mainly caused when
- Keep unused objects in collections without removing them.
- Not removing event listeners after using them.
- Using static fields to store large objects that are not cleared.
- Pointing references to objects from a very long time, even when they are not needed.
It can be prevented by following the practices below.
- Remove unused objects from collections.
- Avoid using static variables for objects unless needed.
- Use tools like VisualVM, Eclipse MAT, or JProfiler to detect memory leaks.
- Regularly review and test memory usage during the development.
OutOfMemoryError in Java
The OutOfMemoryError in Java is a runtime error that occurs when the JVM runs out of memory and is not able to create new objects.
It happens when
- Many objects are created without releasing the previously used ones.
- The size of the heap is very small for the program.
- An infinite loop keeps adding data to memory
It has the following types.
- java.lang.OutOfMemoryError: It occurs when the Heap is full.
- java.lang.OutOfMemoryError: It occurs when the GC overhead limit is exceeded. It keeps running but is noecan’t free enough memory.
- java.lang.OutOfMemoryError: It occurs when too many classes or methods are loaded.
JVM Heap Memory Switches
These are the settings that you can give to Java to tell it how much memory to use for storing the objects when your programs run.
Let us discuss some of them in detail.
1. -Xms: Initial Heap Size
It sets the initial amount of memory that Java starts with. This is the minimum heap size, and the JVM will not start with less than this.
Syntax:
java -Xms512m MyApp
It will start your application MyApp with 512MB of heap memory.
2. -Xmx: Maximum Heap Size
It sets the maximum memory Java can use and prevents the app from using too much memory.
Syntax:
java -Xmx2g MyApp
Your app can use up to 2 GB of memory.
3. -Xmn : Young Generation Size
This gives a fixed amount of memory to the Young Generation
Syntax:
java -Xmn256m MyApp
Java will give 256 MB to the Young Generation.
4. -XX:PermSize : Initial PermGen Size (Java 7 and below)
It sets the starting memory size for class-related info.
Syntax:
java -XX:PermSize=128m MyApp
128 MB is used for storing class and method info at startup.
5. -XX:MaxPermSize : Maximum PermGen Size
This sets the limit for class metadata memory and helps to avoid memory errors.
Syntax:
java -XX:MaxPermSize=256m MyApp
Java will not use more than 256 MB for PermGen.
6. -XX:SurvivorRatio : Eden/Survivor Space Ratio
This decides how memory is split inside the Young Generation.
Syntax:
java -XX:SurvivorRatio=6 MyApp
The Eden space will be 6 times larger than each Survivor space.
7. -XX:NewRatio: Old to Young Generation Ratio
This sets how much memory goes to the Old vs the Young Generation.
Syntax:
java -XX:NewRatio=3 MyApp
The Old Generation will get 3 times more memory than the Young.
Reference Types in Java
In Java, references are used to access objects. The type of reference determines how objects are managed by the Garbage Collector.
There are mainly 4 primary types of references in Java. These are as follows:
1. Strong Reference
It is the default reference type in Java. If any object has a strong reference to it, it will not be collected by the garbage, even if no other references are present to the object.
The objects with strong references are kept in memory. And, as long as the strong reference is present, the object is not eligible for garbage collection.
Example:
String str = new String("Hello, World!");
2. Weak Reference
A Weak Reference is a reference type that allows an object to be collected as garbage as soon as it becomes weakly reachable, which means when there are no strong references pointing to the object.
The Weak references do not prevent an object from being garbage collected. Also, weak references are used for objects that can be discarded when memory is needed, such as caches or mappings in a WeakHashMap.
Example:
WeakReference<String> wR = new WeakReference<>(new String("Hello, Weak Reference"));
In this case, once the WeakReference is the only reference to the object, the object will be eligible for garbage collection.
3. Soft Reference
A Soft Reference is similar to a weak reference. In this, the object will be only garbage when the JVM needs memory. It is useful for caching objects that can be removed when memory is low.
Soft references are typically used to create memory-sensitive caches where objects are removed only when memory is scarce.
Example:
SoftReference<String> sRef = new SoftReference<>(new String("Hello, Soft Reference"));
4. Phantom Reference
A Phantom Reference is the weakest kind of reference in Java. When an object is only reachable through a phantom reference, it can’t be used anymore, and Java will not return it, unlike other reference types.
Phantom references are mainly used to do some cleanup work after an object is ready to be removed from memory.
Example:
PhantomReference<String> pRef = new PhantomReference<>(new String("Hello, Phantom Reference"), referenceQueue);
In this case, the phantomRef will not allow direct access to the object. You can check the referenceQueue to know when the object is to be garbage collected.
Pass-by-Value or Pass-by-Reference in Java
In programming, pass-by-value means a copy of the actual value is passed to methods. And, Pass-by-reference means a reference or address of the actual variable is passed to methods.
In Java:
- For primitive data types: The actual value is copied and passed to the method.
- For object types: The value of the reference, i.e., the memory address pointing to the object, is copied and passed.
Java does not pass objects by reference. It passes the reference itself by value. This means that:
- You can change the internal state of the object inside the method, as the reference points to the same object in the heap.
- However, you can not make the reference point to a new object and change it outside the method.
Let us understand this with the help of an example. Consider the following example below.
Example:
Output:
Explanation:
In the above Java code.
- The myCar points to a Car object having the color red.
- When we pass myCar to modifyColor(car), Java copies the reference, so both myCar and car refer to the same Car object.
- In the method modifyColor(), we change car.color to blue. Since car and myCar refer to the same object, this change is visible in main().
- After that, we pass myCar to changeReference(car). Once again, Java copies the reference. And in the changeReference() method, we assign a new Car object to the parameter car, and set its color to green.
- However, this new object is only known inside the changeReference() method. Hence, it does not affect the original myCar reference in main().
The Mark and Sweep Model
The Mark and Sweep Model is a garbage collection algorithm that is used to manage the memory of programming languages like Java. It helps to identify the objects that are not used now, so that their memory can be given to others. This algorithm also prevents memory leaks in Java.
The Mark and Sweep algorithm consists of two primary phases: the Mark Phase and the Sweep Phase.
1. Mark Phase:
In this phase, the garbage collector marks the objects whose objects can be referenced by active variables, static fields, etc. It starts from the root and marks all the objects that are referenced, meaning they are still in use.
2. Sweep Phase:
The garbage collector sweeps the heap and frees up the memory of all the unmarked objects.
The unmarked objects are the garbage, meaning they can not be accessed further by any reference. These objects are then removed, and their memory is reclaimed.
Let us understand it with an example.
Imagine a scenario with objects in memory, some of which are referenced by active variables, and others are no longer in use.
Before Marking:
Objects in the heap: A, B, C, D, E
Active references: root -> A -> C
The objects B, D, and E are not reachable from the root.
In the Mark Phase, the garbage collector starts from the root and marks A as reachable. Then, it follows the reference from A to C and marks C as reachable.
Objects B, D, and E are not reachable and are unmarked.
In the Sweep Phase, the garbage collector scans the heap and frees the memory of unmarked objects B, D, and E. The objects A and C remain, as they were marked during the Mark phase.
Compaction
After the garbage collector removes the garbage objects form the memory, there are some empty spaces left between the objects that are remained in the memory. These gaps make it difficult to store new objects in memory. It works after the Mark and the Sweep method.
Compaction removes these gaps in the memory by making the stored objects in the same location side by side. And then updates all the references of the objects in the memory so that they point to the correct location.
Compaction makes the memory fragmented. Without compaction, the JVM will not be able to allocate big objects even if the memory is free. It makes allocation faster and efficient.
The image below the Box A, B and C shows live objects in the memory before the compaction. The empty spaces are the fragments where the garbage objects were removed.
Now, if you want to add a big new box, there is no room even if total free space exists.
This is what compaction does: it removes the gaps by moving all the live objects next to each other.
Now, the image below shows the memory allocation after the compaction.
Now, the one big empty space is available, which can be used to place any big object in the memory.
Get 100% Hike!
Master Most in Demand Skills Now!
Conclusion
Java Memory Management plays an important role in maintaining the performance of the application. By understanding the structure of JVM memory areas, Heap, Stack, Method Area, and more, you can write efficient code. Choosing the right garbage collector, tuning JVM switches, managing references correctly, and preventing memory leaks all play a vital role in avoiding issues like OutOfMemoryError. With proper memory monitoring and best practices, you can ensure your Java applications run smoothly even under heavy load or complex workloads.
If you want to learn more about Java, you can refer to our Java Course.
Java Memory Management – FAQs
Q1. What is memory management in Java?
It is the process of allocating, using, and releasing memory in Java. It is managed by JVM (Java Virtual Machine).
Q2. What is JVM memory?
JVM memory refers to the memory managed by the Java Virtual Machine (JVM) for running Java applications.
Q3. What are the 5 types of memory in Java?
Types of Memory Areas Allocated by JVM are as follows
Class(Method) Area.
Heap.
Stack.
Program Counter Register (PC Register)
Native Method Stack.
Q4. What is Java heap size?
Heap size refers to the amount of memory allocated to an application’s runtime data area, known as the Java Virtual Machine (JVM) heap.
Q5. How to clear memory in Java?
You can call the System.gc(); method to suggest that Java to run the garbage collection and free up memory.