Java Memory Management and Garbage Collection
Introduction to Memory Management
Memory management in Java is like managing your computer’s workspace. It’s about how Java keeps track of where it stores information while your program is running. Just like you organize files in folders, Java manages memory to store data and objects. It’s important to understand memory management to make sure your Java programs run smoothly and don’t waste resources.
Java Memory Areas
Java uses predominantly 5 different memory areas as shown in below diagram.
Method Area (PermGen in Java 1.7, Metaspace in Java 1.8): This is where Java keeps information about classes and methods. In older versions like Java 1.7, it was called PermGen (Permanent Generation), but in Java 1.8, it’s called Metaspace. It stores things that don’t change often, like class definitions.
Heap Area: This is like a big storage area where Java stores objects your program uses. It’s called the heap because objects are piled up there, and it’s managed by the garbage collector, which helps clean up objects that are no longer needed.
Stack Area: This is where Java keeps track of what your program is doing. It’s like a notepad where Java writes down which methods are currently running and what data they’re using. It helps your program keep organized.
PC Registers: These are like bookmarks for Java. They keep track of which instruction in your code is being executed. This helps Java know where to continue when a method call is finished.
Native Method Stack: This is a special place for code that isn’t written in Java, like code in other programming languages. It helps Java talk to this external code and manage it separately from regular Java code.
Heap Memory
Object Creation: Heap memory is where Java stores objects created by your program. When you make a new object, like a car or a book in your program, Java finds a spot in the heap memory to keep it.
New Operator: To create objects, you use something called the “new” operator. It’s like a magic spell that tells Java to make a new object and find space for it in the heap memory.
Memory Allocation for Objects: When an object is created, Java figures out how much space it needs in the heap memory and assigns it a place. This space is reserved for the object to use.
Garbage Collection Process: Over time, objects in the heap memory might not be needed anymore. Java has a process called “garbage collection” to clean up and remove these unused objects, freeing up space for new ones.
Java heap memory is further divided into multiple subsections as described below.
Young Generation: Heap memory is divided into parts, and one part is called the “Young Generation.” It’s where new objects are initially placed.
Eden Space: Within the Young Generation, there’s a place called “Eden Space.” This is where brand new objects go first when they are created.
Survivor Spaces (S0 and S1): After some time, objects from Eden Space that are still needed move to Survivor Spaces (S0 and S1). These spaces act like a waiting area for objects that are not too old.
Old Generation (Tenured Space): Objects that have been around for a while and survived many garbage collection cycles move to the Old Generation, also known as Tenured Space. It’s a more permanent spot for long-lasting objects.
Permanent Generation (Java 1.7) and Metaspace (Java 1.8): In older Java versions like Java 1.7, there was a part of heap memory called “Permanent Generation,” which stored class metadata. In Java 1.8 and newer, it’s called “Metaspace,” and it’s used to store the same kind of information but is managed differently.
Analyzing Heap Memory
We will write a simple java program and and analyze heap memory. We will also verify if we are able to see all above mentioned heap subareas.
In below code we are creating array list of 1 Million Double elements repetitively for 100 times. We are keeping a sleep of 1 seconds in every loop, so that we can observe gradual memory increase and garbage collections happening.
import java.util.ArrayList;
import java.util.List;
public class MemoryVerification {
public static void main(String[] args) {
createRecurringObjects();
}
private static void createRecurringObjects() {
for (int i=0 ; i< 100; i++) {
List<Double> list = new ArrayList<>(1000000);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
Compile and run above java code using below commands.
javac MemoryVerification.java
java MemoryVerification
jstat Utility:
We will be using jstat utility that comes by default with java. Run jps command to list all java processes running on system. Look for process id of java process with name MemoryVerification.
Run below jstat command. (2000 at last will keep on running this command every 2000 milliseconds i.e every 2 seconds)
jstat -gc <pid-of-memory-verification-program> 2000
Below is the output of above jps and jstat command.
Analysis of above output:
- We can see EU (Eden Utilization) memory increasing. The Eden space gets free when Young GC happens (YGC Young Generation Garbage Collection Count) increases to 2 and 3.
- We can see S0U (Survivor 0 Utilization) and S1U (Survivor 1 Utilization) getting used alternatively. Either of S0 or S1 gets used in any point in time and another remains empty.
- No full garbage collection FGC (Full Garbage Collection Count) triggered during this process as all of objects gets garbage collected in young generation.
The list of all above Acronyms can be found on jstat link.
Garbage Collection in Java
Garbage collection in Java is like tidying up after a party. It’s an automatic system that cleans up memory by finding and removing objects that your program doesn’t need anymore. This helps keep your computer’s memory organized.
Automatic Memory Management: Java takes care of memory for you. You don’t have to manually clean up objects when you’re done with them; Java’s garbage collector does it automatically.
Garbage Collection Algorithms: These are special methods that the garbage collector uses to find and remove unused objects. Different algorithms work in different ways to make sure your program runs smoothly.
Serial Garbage Collector: This collector cleans up one thing at a time. It’s like doing chores one after another, and it’s good for smaller programs or single-threaded applications.
Parallel Garbage Collector: This collector is like having a team of cleaners working together to pick up garbage. It’s faster because it uses multiple threads to clean up memory.
CMS (Concurrent Mark-Sweep) Collector: It’s like cleaning up while the party is still going on. CMS tries to minimize pauses in your program by cleaning up memory while it’s still running.
G1 (Garbage First) Collector: G1 divides memory into small pieces and cleans them up in a smart way. It’s like sorting and cleaning up your room by sections, making it efficient for large applications.
Object Finalization: It’s like saying goodbye to objects before they are removed. When an object is no longer needed, Java allows you to do some final actions on it before it’s cleaned up.
JVM Command-Line Options
We can tune JVM as per the nature of our application. Below are the JVM command line options available.
- -Xmx<size>: Sets the maximum heap size for the JVM. For example,
-Xmx512m
sets the maximum heap size to 512 megabytes. - -Xms<size>: Sets the initial heap size. It specifies the amount of memory that the JVM initially allocates when your program starts.
- -Xss<size>: Sets the thread stack size. This option specifies the amount of memory allocated for each thread's stack.
- -XX:MaxPermSize=<size>: Sets the maximum permanent generation size (Java 1.7 and earlier). This option is used to control the size of the permanent generation space.
- -XX:MaxMetaspaceSize=<size>: Sets the maximum metaspace size (Java 1.8 and later). It controls the size of the metaspace, which replaces the permanent generation in Java 1.8 and later.
- -XX:NewSize=<size>: Sets the initial size of the young generation. The young generation is where new objects are initially allocated.
- -XX:MaxNewSize=<size>: Sets the maximum size of the young generation.
- -XX:SurvivorRatio=<ratio>: Adjusts the size of the survivor spaces in the young generation. The survivor ratio is used to determine the size of Eden and survivor spaces.
- -XX:NewRatio=<ratio>: Sets the ratio of the young generation size to the old generation size. It helps control the size of the overall heap.
- -XX:+UseSerialGC: Specifies the use of the serial garbage collector. It's suitable for single-threaded or small-scale applications.
- -XX:+UseParallelGC: Enables the use of the parallel garbage collector, which is optimized for multi-core systems.
- -XX:+UseConcMarkSweepGC: Activates the concurrent mark-sweep garbage collector, which reduces application pause times by performing garbage collection concurrently with application threads.
- -XX:+UseG1GC: Turns on the G1 (Garbage-First) garbage collector, which is designed for both throughput and low-latency applications.
- -XX:ParallelGCThreads=<num>: Sets the number of threads used by parallel garbage collectors.
- -XX:MaxGCPauseMillis=<ms>: Sets the maximum acceptable pause time for garbage collection. The garbage collector will try to meet this target.
- -XX:+HeapDumpOnOutOfMemoryError: Generates a heap dump file when an OutOfMemoryError occurs, helping diagnose memory-related issues.
- -XX:HeapDumpPath=<path>: Specifies the directory or file path where heap dump files are created.
Monitoring and Profiling Tools
These are like detective tools for your Java program. They help you keep an eye on how memory is used and how garbage collection is happening. Tools like VisualVM and JVisualVM can give you insights into your program’s memory behavior.
Conclusion:
When we are writing java code we should be aware about memory requirements and how JVM memory, Garbage Collection works internally. This helps writing better code. Please do try out other options available on jstat utility. Trust me analyzing jstat outputs of a enterprise applications is lot of fun. Happy Learning!
If you are interested in reading how G1GC works read article here.