As experience has shown, sometimes a sort of uncertainty may arise on the subject of Java Virtual Machine (JVM) memory structure and other related aspects such as sizes of various kinds of memory, live and dead objects, etc.
Heap Memory, which is the storage for Java objects
Non-Heap Memory, which is used by Java to store loaded classes and other meta-data
JVM code itself, JVM internal structures, loaded profiler agent code and data, etc.
Also, the JVM has memory other than the heap, referred to as non-heap memory. It is created at the JVM startup and stores per-class structures such as runtime constant pool, field and method data, and the code for methods and constructors, as well as interned Strings.
Retained size of an object is its shallow size plus the shallow sizes of the objects that are accessible, directly or indirectly, only from this object. In other words, the retained size represents the amount of memory that will be freed by the garbage collector when this object is collected. In general, retained size is an integral measure, which helps to understand the structure (clustering) of memory and the dependencies between object subgraphs, as well as find potential roots of those subgraphs.
A generational collector takes advantage of the fact that in most programs,the vast majority of objects (often greater than 95 percent) are very short lived (for example, they are used as temporary data structures). By segregating newly created objects into an object nursery, a generational collector can accomplish several things. First, because new objects are allocated contiguously in stack-like fashion in the object nursery, allocation becomes extremely fast, since it merely involves updating a single pointer and performing a single check for nursery overflow. Secondly, by the time the nursery overflows, most of the objects in the nursery are already dead, allowing the garbage collector to simply move the few surviving objects elsewhere, and avoid doing any reclamation work for dead objects in the nursery.
The current version of the Java HotSpot VM introduces a parallel mark-compact collector for the old generation designed to improve scalability for applications with large heaps. Where the concurrent mark-sweep collector focuses on decreasing pause times, the parallel old collector focuses on increasing throughput by using many threads simultaneously to collect the old generation during stop-the-world pauses. The parallel old collector uses many novel techniques and data structures internally to achieve high scalability while retaining the benefits of exact garbage collection and with a minimum of bookkeeping overhead during collection cycles.
Exploring JVM architectureFundamentally, the JVM is placed above the platform and below the Java application (Figure 2).Going further down, the JVM architecture pans out as shown in Figure 3. Now let us look into each of the blocks in detail.In a nutshell, JVM architecture can be divided into two different categories, the details of which are provided below.Class loader subsystem: When the JVM is started, three class loaders are used.a. System class loader: System class loader maps the class-path environment variables to load the byte code.b. Extension class loader: Extension class loader loads the byte code from jre/lib/ext.c. Bootstrap class loader: The bootstrap class loader loads the byte code from jre/lib.Method area: The method area (or class area) stores the structure of the class once it is loaded by the class loader. The method area is very important; it does two things once the class is stored in this area:a. Identification: All static members (variable, block, method, etc) are identified from top to bottom.b. Execution: Static variables and static blocks are executed after the identification phase, and static methods are executed when they are called out. Once all static variables and blocks are executed, only then will the static method be executed.Heap area: The heap area stores the object. Object instances are created in this area. When a class has instance members (instance variable, instance method and instance block), these members are identified and executed only when the instance is created at the heap area.
JVM stack exists for one thread and holds one Frame for each method executing on that thread. It is LIFO data structure. Each stack frame has reference to for local variable array, operand stack, runtime constant pool of a class where the code being executed.
This post should give you an overview of the JVM memory structure and memory management. This is not exhaustive, there are a lot more advanced concepts and tuning options available for specific use cases and you can learn about them from But for most JVM(Java, Kotlin, Scala, Clojure, JRuby, Jython) developers this level of information would be sufficient and I hope it helps you write better code, considering these in mind, for more performant applications and keeping these in mind would help you to avoid the next memory leak issue you might encounter otherwise.
As with all the other runtime memory areas, implementation designers can use whatever data structures they deem most appropriate to represent the local variables. The Java virtual machine specification does not indicate how longs and doubles should be split across the two array entries they occupy. Implementations that use a word size of 64 bits could, for example, store the entire long or double in the lower of the two consecutive entries, leaving the higher entry unused.
Interestingly, JVM does not care about the Java language or any other programming language with respect to its semantics and syntactical structure. When it comes to executing a program, its primary interest lies in a particular file format called the class file format. The file format *.class has nothing to do with object-oriented class structure defined in Java code. It is a *.java file transformed into a *.class file by the compiler. JVM is ready to interpret class files; it does not matter what compiler is used create it as long as it creates a class file format. The Java compiler compiles a program into its equivalent class files. These class files actually contain half-compiled code called bytecode. It is called half compiled because bytecode is not directly executable, as are binary files created by the C/C++ compiler. It is meant to be fed into the JVM, which, in turn, interacts with the underlying platform to finally execute the instructions. The bytecode thus contains JVM instructions, a symbol table, and other ancillary information. A compiler that can produce bytecode according to the syntactic and structural constraints of the JVM is a candidate to be executed on JVM, irrespective of any language.
This tutorial is the first part in the series. It will explain the basic terminologies like JDK, JVM, JRE, HotSpot VM then understand the JVM architecture and Java heap memory structure. It is important to understand these basic things before going into the Garbage collection tutorial.