To execute an application in an optimal way, Java Virtual Machine (JVM) divides the memory into two main memory spaces and they are stack memory and heap memory. There are some other memory allocations also in the JVM and they are Perm Gen, Native Memory and PC-Registers. The JVM designates memory for operations such as declaring new variables and objects, methods, declaring strings in the stack memory or in the heap space. Let’s go more specifically through these memory models.
Stack Memory in Java
Stack Memory in java is used for static memory allocation and the execution of a thread. It contains primitive values that are specific to a method and references to objects that are in a heap, referenced from the method. Stack stores the data which have a short life time such as local variables.
Access to this memory is in Last-in First-out(LIFO) order. Whenever a new method is called, a new block on the top of the stack is created which contains the values specific to that method. We call it a Stack Frame. When a method is called inside of another method, another new block on the top of the stack is created. After method finishes the execution, its corresponding stack frame is flushed and the flow goes back to the calling method. Then the space becomes available for the next method.
In a java program each thread has its own stack. Therefore you can see that multi-threading can decrease the time taken for execution.
Some of the key features of Stack Memory are mentioned below.
- Stack Memory grows and shrinks as new methods are called and returned respectively.
- Variables inside stack exist only as long as the method that created them is running.
- It’s automatically allocated and deallocated when method finishes the execution.
- If this memory is full, java throws java.lang.StackOverFlowError
- Access to this memory is fast when compared to heap memory.
- Stack memory is threadsafe as each thread operates in its own stack.
Stack memory only exists as long as the current method is running and comparatively much faster to allocate.
Heap Space(Heap Memory) in java is used for dynamic memory allocation for java objects and JRE classes at the runtime. New objects are always created in heap space and the references to this objects are stored in stack memory. These objects have global access and can be accessed from anywhere in the application. Heap Space stores the data which have a long lifetime. There is only one heap space for all the threads. Most objects are quite big and most programs want to pass around objects between blocks of codes. By placing objects in heap, it makes easy to pass them around and all threads and stacks can access them in the heap.
This memory model is divided into smaller parts called generations.
- Young Generation — where all new projects are allocated and aged. A minor garbage collection occurs when this fills up.
- Old(Tenured) Generation — where long surviving objects are stored. When objects are stored in the Young Generation, a threshold for the object’s age is set and when that threshold is reached, object is moved to old generation.
- Permanent Generation — consists of JVM metadata for the runtime classes and application methods.
Young Generation is also divided into another three small parts.
- Eden Space — Where all the new objects are stored
- Survivor Space 0 — Where the objects are stored after the action of garbage collection in the Eden Space
- Survivor Space 1 — Where the objects are stored after the action of garbage collection in the Survivor Space 0
Some key features of heap memory are mentioned below.
- it’s accessed via complex memory management techniques that include Young Generation, Old Generation and Permanent Generation.
- If heap space is full, java throws java.lang.OutOfMemoryError
- Access to the memory is relatively slower than stack memory.
- This memory isn’t automatically deallocated. It needs Garbage Collector to free up unused objects so as to keep the efficiency of the memory usage.
- Unlike stack, a heap isn’t threadsafe and needs to be guarded by properly synchronizing the code.
Heap Space exists as long as the application runs and slower to allocate compared to stack.
When the Young Generation filled up, the Garbage Collector come in and this type of garbage collection is called Minor Garbage Collection (Minor GC). Objects which goes through different life cycles and a lot life cycles in garbage collection but still survived, will be moved into the Old Generation where the long living objects are stored. The Garbage Collection which happens in the Old Generation is called Major Garbage Collection (Major GC). This helps to identify the objects which are in the old generation not helpful anymore. All the new objects are stored in Eden Space and objects survived from the Minor GC are moved the Survivor Space 0. Minor Garbage Collector also checks the Survivor Space and the objects survived are moved into the other Survivor Space. Therefore it can make sure that the Survivor Space is always empty. Objects which are survived a lot of cycles will be moved into the Old Generation like I have mentioned earlier. It’s done by setting a threshold for the age of the young generation objects before they become eligible to promote for the old generation.
Different Steps inside a Garbage Collection
- Marking — Garbage Collection identifies which are the objects not in use
- Normal Deletion — unused objects will be removed from the heap
- Deletion + Compacting — Once objects are removed from the heap, memory allocation is random. These memory allocations are grouped together so that the memory allocation in the future will be faster. (As a part of deleting objects from the heap, compacting help as grouping the memory together so that the performance of the creation or the allocation of new memory increased in the subsequent calls.)
Different types of Garbage Collection
- Serial GC — This is useful when we need to mark and delete simply in the young and old generations. This is used in small applications which have low memory foot prints because it’s directly going to arrange by the objects and then delete them from the heap. This might not be a solution for large scale applications when you have a lot of threads running on the heap.
- Parallel GC — Most of the features are the same as Serial Gcs but they can spawn N number of threads for the young generation and N number of threads are based on the CPU which are present in the system. We can control this number of threads by setting up some parameters when we start the JVM. These are also called as the throughput collectors because it uses multiple CPUs and it speeds up the garbage collection performance. It uses only one thread for the old generation since there is less chance to be garbage collected in the old generation.
- Parallel Old GC — This is actually the same as the Parallel GC except the point that old generation also have N number of threads.
- Concurrent Mark Sweep(CMS) GC — This is also refereed to us as concurrent low cost collectors. It does the garbage collection for old generation only. These collectors try to minimize the pause happen during the garbage collection. We can limit the number of threads in the CMS collector by using some JVM options as well. This is useful if we do not want to pass an application for a long period.
- Garbage First(G1) GC — It is a parallel concurrent and incrementally compacting low cost garbage collector. There is no concept like young and old generations space. It divides the heap space into multiple equally sized heaped regions and whenever there is a garbage collection involved, it collects the region first with the lesser live data (Garbage First).
Native Memory is where all our other language specific codes reside. As an example shocket connections reside in the native memory behalf of residing in the stack or heap memory.
Perm Gen(Method Area)
Perm Gen is the space where the compiled objects of methods and classes are stored. They are a part of the heap memory and then it can occur the Out of Memory Error. But different memory sizes can be provided for the JVM. Therefore they have the classes got loaded as a part of the class loader. Perm Gen was replaced with the Metaspace with the Java8.
Metaspace are a part of native memory unlike the perm gen which has a memory inside the heap. These are at the process level and they do not merge inside the heap memory. Therefore Metaspaces can be given separate spaces that does not merge with the heap memory. Process sizes can go large since the metaspace goes to the OS and then it will not occur the Out of Memory Error. But too many classes loading on the OS will run out of memory in the OS and it can bring down the hole server.
PC Registers store the sequence of information/instruction which needs to be executed next. There is one PC register per thread. It tells the thread what it needs to do next.