Engineering Full Stack Apps with Java and JavaScript
Objects in Java are allocated on the heap. The heap is an area of memory that is used for dynamically allocated memory, such as objects. In Java, objects allocated in a program are later released by the JVM through a process called Garbage collection which is performed automatically by the JVM. An application has little control over this process. There are also other tyoes of memory available in Java.
When you talk about effective Java memory management, you are talking about managing the RAM footprint. RAM Footprint is the total amount of memory used by your Java applications as well as the JVM. RAM footprint consist of two kinds of memory:
Heap memory
Native memory
Heap memory
Heap memory is mainly used for allocating objects and a program called Garbage collector is responsible for cleaning the heap memory in Java. You don't have to manually deallocate memory like you have to do in some other programming languages. The primary benefit of the Garbage collection technique is minimizing memory leaks. A memory leak occurs when memory is dynamically allocated but is never released. This has been a common problem with languages such as C and C++, where it is the responsibility of the programmer to manage the heap. A memory leak can still occur in Java if an object is allocated but the reference to the object is never released when the object is no longer needed.
Heap Memory consist of:
Regular heap
Permgen / Metaspace
Regular heap
Regular heap is the place where your objects are allocated space. It is again divided into two regions called young generation and old generation. Young generation is again divided into two regions called Eden and Survivor space. The heap does not grow in a predictable order and can become fragmented. In general, you will not have to know these internals, but only want to know that your objects are allocated in regular heap.
The process of de-allocating or freeing memory of objects not used anymore in Java is called garbage collection. Garbage collector will de-allocate memory for objects which are eligible for garbage collection. An object will become eligible for garbage collection when there are no more references (actually strong references) to it or the reference variable’s lifetime and scope is over (e.g. the stack entry is destroyed as a method was exited). There are two ways in which a reference to an object is de-referenced; the reference is assigned another object or the reference is assigned null.
Garbage collection process is usually beyond the control of the application (if we need to change its behavior, we have to use JVM flags), the best we can do is to make objects eligible for garbage collection as soon as its use is over. To make an object eligible for garbage collector, declare it in the least scope possible and assign null to the reference variable when it is no longer needed. Leaving references when the objects are no longer needed can lead to memory leaks. You can also use soft references. There are mainly four different algorithms for doing garbage collection in Java, which you can change using JVM flags.
Object's finalize() method is invoked by the garbage collector just before reclaiming memory. The finalize method of class Object performs no special action; it simply returns normally. Subclasses of Object (all classes in Java) may override this method and add some clean up code. This method might not be invoked at all and hence you should not rely on the method. Java however gives below guarantees regarding the finalize() method:
The thread that invokes finalize will not be holding any user-visible synchronization locks when finalize is invoked.
The finalize method is never invoked more than once by a Java virtual machine for any given object.
If an uncaught exception is thrown by the finalize method, the exception is ignored and finalization of that object terminates.
Also read the note Finalize Method in Java - Why Not To Use, Limited Use Cases and Alternatives.
Permgen and MetaSpace
Permgen and MetaSpace contain metadata about classes. Metadata is the data about classes, and is used by JVM to manage the classes. Permgen is used till java 7 and Metaspace is used from Java 8. Permgen also contained additional data than class metadata, which were moved to regular heap in Java 8; Metaspace contains only class data. One of the data that contained in the permgen was static variables.
Native memory
Native memory is used by JVM for its internal operations and also for thread stacks, code cache and direct byte buffers.
Native Memory consist of:
Thread stacks
Code cache
Direct byte buffers
Knowing the concept of Stack data structure can be useful to understand stack memory in Java. Stack is a last in first out (LIFO) data structure. Activation records are pushed onto a stack when the method is invoked and popped off the stack when the method returns. The temporary existence of these variables determines the lifetime of the variables. This means that, whenever you invoke a method, a stack entry is created and all local variables are created in it. When that method is exited, all those local variables are deleted along with the stack entry. Primitives and reference variables are stored in the stack memory whereas actual objects are stored in the heap memory.
The stack grows toward the heap when a method is invoked and shrinks when the method returns. As they share the same memory space, if the heap and stack collide then the program will terminate.
Code cache contains the compiled code by JVM during JIT compilation.
Direct byte buffers are used to share data between Java and C++ programs without copying data; in case of heap memory, you will have to first copy data to a native memory which can be accessed by the C++ program.
In general, you will not have to know many of the internals mentioned here, but it is good to have some basic idea. Will discuss these in more detail under Java performance section. Feel free to ask if you would like to learn more on any topic mentioned here. You can find Java performance related notes here.