JVM (Java) and it’s Memory Management
JVM (Java Virtual Machine) is an abstract machine or can say it is a software implementation of a Physical Machine. It is a specification that provides runtime environment in which java bytecode can be executed. JVMs are available for many hardware and software platforms (i.e. JVM is platform dependent).
What is JVM
- A specification: Where working of Java Virtual Machine is specified. But implementation provider is independent to choose the algorithm. Its implementation has been provided by Oracle and other companies.
- An implementation: Its implementation is known as JRE (Java Runtime Environment).
- Runtime Instance: Whenever you write java command on the command prompt to run the java class, an instance of JVM is created.
What it does
The JVM performs following operation:
- Loads code
- Verifies code
- Executes code
- Provides runtime environment
JVM provides definitions for the:
- Memory area
- Class file format
- Register set
- Garbage-collected heap
- Fatal error reporting etc.
How JVM works in Java?
To understand the JVM architecture first let’s understand how it works. As shown in the architecture diagram JVM is divided into three main subsystems
- Class Loader Subsystem
- Runtime Data Area
- Execution Engine
1. Class Loader subsystem
Classloader is a subsystem of JVM which is used to load class files. Whenever we run the java program, it is loaded first by the classloader. Java’s dynamic class loading functionality is handled by the class loader subsystem. It loads, links and initializes the class when it refers to a class for the first time at runtime, not at compile-time.
Classes will be loaded by this component. Boot Strap class Loader, Extension class Loader, and Application class Loader are the three class loader which will help in achieving it.
Boot Strap ClassLoader:- Highest priority will be given to this loader.
- loads platform classes (such as java.lang.Object, java.lang.Thread etc)
- loads classes from rt.jar ($JRE_HOME/lib/rt.jar)
- -Xbootclasspath may be used to alter the boot class path -Xbootclasspath/p: and -Xbootclasspath/a: may be used to prepend/append additional bootstrap directories – have extreme caution doing so. In most scenarios, you want to avoid playing with boot class path.
- In Sun’s implementation, the read-only System property sun.boot.class.path is set to point to the boot class path.Note that you can not change this property at runtime – if you change the value that won’t be effective.
- This loader is represented by Java null. i.e., For example, java.lang.Object.class.getClassLoader() would return null (and so for other bootstrap classes such as java.lang.Integer, java.awt.Frame, java.sql.DriverManager etc.)
- loads classes from installed optional packages
- loads classes from jar files under $JRE_HOME/lib/ext directory
- System property java.ext.dirs may be set to change the extension directories using -Djava.ext.dirs command line option.
- In Sun’s implementation, this is an instance of sun.misc.Launcher$ExtClassLoader (actually it is an inner class of sun.misc.Launcher class).
- Programmatically, you can read (only-read!) System property java.ext.dirs to find which directories are used as extension directories. Note that you can not change this property at runtime – if you change the value that won’t be effective.
- Application ClassLoader:- Responsible for loading Application Level Classpath, path mentioned Environment Variable etc.
Application ClassLoader:- Responsible for loading Application Level Classpath, path mentioned Environment Variable etc.
- Loads classes from application classpath
- Application classpath is set using
Environment variable CLASSPATH (or)
-cp or -classpath option with Java launcher
If both CLASSPATH and -cp are missing, “.” (current directory) is used.
- The read-only System property java.class.path has the value of application class path. Note that you cannot change this property at runtime if you change the value that won’t be effective.
The above Class Loaders will follow Delegation Hierarchy Algorithm while loading the class files.
- Verify– Bytecode verifier will verify whether the generated bytecode is proper or not if verification fails we will get the verification error.
- Prepare– For all static variables memory will be allocated and assigned with default values.
- Resolve– All symbolic memory references are replaced with the original references from Method Area.
This is the final phase of Class Loading, here all static variables will be assigned with the original values, and the static block will be executed.
2. Runtime Data Area
The Runtime Data Area is divided into 5 major components:
- Method Area – All the class level data will be stored here, including static variables. There is only one method area per JVM, and it is a shared resource.
- Heap Area – All the Objects and their corresponding instance variables and arrays will be stored here. There is also one Heap Area per JVM. Since the Method and Heap areas share memory for multiple threads, the data stored is not thread safe.
- Stack Area – For every thread, a separate runtime stack will be created. For every method call, one entry will be made in the stack memory which is called as Stack Frame. All local variables will be created in the stack memory. The stack area is thread safe since it is not a shared resource. The Stack Frame is divided into three subentities:
- Local Variable Array – Related to the method how many local variables are involved and the corresponding values will be stored here.
- Operand stack – If any intermediate operation is required to perform, operand stack acts as runtime workspace to perform the operation.
- Frame data – All symbols corresponding to the method is stored here. In the case of any exception, the catch block information will be maintained in the frame data.
- PC Registers – Each Java Virtual Machine thread has its own pc (program counter) register. At any point, each Java Virtual Machine thread is executing the code of a single method, namely the current method for that thread. If that method is not native, the pc register contains the address of the Java Virtual Machine instruction currently being executed. If the method currently being executed by the thread is native, the value of the Java Virtual Machine’s pc register is undefined. The Java Virtual Machine’s pc register is wide enough to hold a return Address or a native pointer on the specific platform.
- Native Method stacks – Native Method Stack holds native method information. For every thread, a separate native method stack will be created.
3. Execution Engine
The bytecode which is assigned to the Runtime Data Area will be executed by the Execution Engine. The Execution Engine reads the bytecode and executes it piece by piece.
- Interpreter – The interpreter interprets the bytecode faster, but executes slowly. The disadvantage of the interpreter is that when one method is called multiple times, every time a new interpretation is required.
- JIT Compiler – The JIT Compiler neutralizes the disadvantage of the interpreter. The Execution Engine will be using the help of the interpreter in converting byte code, but when it finds repeated code it uses the JIT compiler, which compiles the entire bytecode and changes it to native code. This native code will be used directly for repeated method calls, which improve the performance of the system.
- Intermediate Code generator – Produces intermediate code
- Code Optimizer – Responsible for optimizing the intermediate code generated above
- Target Code Generator – Responsible for Generating Machine Code or Native Code
- Profiler – A special component, responsible for finding hotspots, i.e. whether the method is called multiple times or not.
- Garbage Collector: Collects and removes unreferenced objects. Garbage Collection can be triggered by calling “System.gc()”, but the execution is not guaranteed. Garbage collection of the JVM collects the objects that are created.
Java Native Interface (JNI): JNI will be interacting with the Native Method Libraries and provides the Native Libraries required for the Execution Engine.
Native Method Libraries: It is a collection of the Native Libraries which is required for the Execution Engine.
Memory Management in Java
In this article we will discuss the memory model and it’s management in java. This will help you to better understand the concept of Java Garbage collection.
As per below image, total JVM memory is divided into different parts. Total heap is divided into two parts Young Generation and Old Generation.
Memory Management in Java – Young Generation
Young generation is the place where all the new objects are created. When young generation is filled, garbage collection is performed. This garbage collection is called Minor GC. Young Generation is divided into three parts – Eden Memory and two Survivor Memory (S0 and S1) spaces.
Important Points about Young Generation Spaces:
- Most of the newly created objects are located in the Eden memory space.
- When Eden space is filled with objects, Minor GC is performed and all the survivor objects are moved to one of the survivor spaces.
- Minor GC also checks the survivor objects and moves them to the other survivor space. So at a time, one of the survivor space is always empty.
- Objects that are survived after many cycles of GC, are moved to the Old generation memory space. Usually it’s done by setting a threshold for the age of the young generation objects before they become eligible to promote to Old generation.
Memory Management in Java – Old Generation
Old Generation memory contains the objects that are long lived and survived after many rounds of Minor GC. Usually garbage collection is performed in Old Generation memory when it’s full. Old Generation Garbage Collection is called Major GC and usually takes longer time.
Memory Management in Java – Java Heap Memory Switches
Java provides a lot of memory switches that we can use to set the memory sizes and their ratios. Some of the commonly used memory switches are:
VM SWITCH DESCRIPTION
||For setting the initial heap size when JVM starts
||For setting the maximum heap size.
||For setting the size of the Young Generation, rest of the space goes for Old Generation.
||For setting the initial size of the Permanent Generation memory
||For setting the maximum size of Perm Gen
||For providing ratio of Eden space and Survivor Space, for example if Young Generation size is 10m and VM switch is -XX:SurvivorRatio=2 then 5m will be reserved for Eden Space and 2.5m each for both the Survivor spaces. The default value is 8.
||For providing ratio of old/new generation sizes. The default value is 2.
Most of the times, above options are sufficient, but if you want to check out other options too then please check Java HotSpot VM Options.
PermGen in Java 8
PermGen is replaced with Metaspace in Oracle/Sun JDK8, which is very similar. The main difference is that Metaspace can expand at runtime.
The Permanent generation contains metadata required by the JVM to describe the classes and methods used in the application. The permanent generation is populated by the JVM at runtime based on classes in use by the application. In addition, Java SE library classes and methods may be stored here.
Classes may get collected (unloaded) if the JVM finds they are no longer needed and space may be needed for other classes. The permanent generation is included in a full garbage collection
- Region of Java Heap for JVM Class Metadata.
- Hotspot’s internal representation of Java Classes.
- Class hierarchy information, fields, names
- Method compilation information and bytecodes
- Constant pool and symbolic resolution
PermGen Size Limitations :-
- Limited to MaxPermSize – default ~64M – 85M
- Contiguous with Java Heap : Identifying young references from old gen and permgen would be more expensive and complicated with a non-contiguous heap – card table(A kind of remembered set that records where oops have changed in a generation).
- Once exhausted throws OutOfMemoryError “PermGen space”.
- Application could clear references to cause class unloading.
- Restart with larger MaxPermSize.
- Size needed depends on number of classes, size of methods, size of constant pools.
After Metaspace is introduced
This memory is out of heap memory and part of the native memory. As per the document by default the meta space doesn’t have upper limit. In earlier versions of Java we call this as “Perm Gen Space“. This space is used to store the class definitions loaded by the class loaders. This is designed to grow to avoid the Out Of Memory Errors. But, if it grows more than the available physical memory, then the operating system will use virtual memory. This will have adverse effect on the application performance as swapping the data from virtual memory to physical memory and vice versa is costlier operation. We have JVM options to limit the Meta Space used by JVM. In that case, we may get out of memory errors.
The Permanent Generation (PermGen) space has completely been removed and is kind of replaced by a new space called Metaspace. The consequences of the PermGen removal is that obviously the PermSize and MaxPermSize JVM arguments are ignored and you will never get a java.lang.OutOfMemoryError: PermGen error.
Reasons of ignoring these argument is permanent generation has been removed in HotSpot for JDK8 because of following drawbacks
- Fixed size at startup – difficult to tune.
- Internal Hotspot types were Java objects : Could move with full GC, opaque, not strongly typed and hard to debug, needed meta-metadata.
- Simplify full collections : Special iterators for metadata for each collector
- Want to deallocate class data concurrently and not during GC pause
- Enable future improvements that were limited by PermGen.
Advantages of MetaSpace :-
- Take advantage of Java Language Specification property : Classes and associated metadata lifetimes match class loader’s
- Per loader storage area – Metaspace
- Linear allocation only
- No individual reclamation (except for RedefineClasses and class loading failure)
- No GC scan or compaction
- No relocation for metaspace objects
Metaspace Tuning :-
The maximum metaspace size can be set using the -XX:MaxMetaspaceSize flag, and the default is unlimited, which means that only your system memory is the limit. The -XX:MetaspaceSize tuning flag defines the initial size of metaspace If you don’t specify this flag, the Metaspace will dynamically re-size depending of the application demand at runtime.
Change enables other optimizations and features in the future
- Application class data sharing
- Young collection optimizations, G1 class unloading
- Metadata size reductions and internal JVM footprint projects
See how virtual memory space is allocated for metadata and how it loaded per -class loader with this picture
See how virtual memory space(vs1,vs2,vs3) allocated and how per-class loader chunk is allocated. CL – class loader