In this article, we will explore another area which is susceptible to performance problems. More or less, each line of code is dependent on some data in the form of object, variable, and so on. Hence, JVM needs to have some type of provision to store this information so that it will be available to your code.
This storage is mainly provided by the underline Operating System (OS), which is based on physical memory such as Random Access Memory (RAM) and JVM is allocated to use a portion of it. This memory space is then available to your application running inside the JVM.
There are mainly two areas which you need to focus on:
Before we jump into memory utilization, let’s quickly review CPU memory.
How CPU memory works?
Every time CPU executes the given instructions, it stores the results in registers. Registers are the closest memory to CPU and hence the fastest.
The next series in the CPU memory is RAM, which is accessible to CPU by the memory bus. Each CPU has a set size of physical address such as 16 bit, 32 bit, and 64 bit.
Depending on this size, a CPU can access the physical memory. For example, a 16-bit address can access 216 equivalent to 65.536 memory locations, a 32-bit address can access 232 equivalent to 4.294.967.296 memory locations, and so on.
The orchestration of memory is controlled by the underline operating system, which maps the physical memory to each process’s memory. This can be achieved with the help of virtual memory.
The OS does the underline wiring to assign each process a memory slot in a virtual memory space and maps this to the real physical memory like RAM.
What is Java Heap?
As soon you start your application, JVM asks for some memory from the operating system. It allocates space for your application. Part of this space is called as Java heap memory, which is located at the bottom of the address space. This place is used for storing all the objects created by using the operator
As long as you have an active reference to this newly created object, it uses memory space in heap. Once you remove this reference, this object becomes an orphan and available for removal so that JVM can claim this allocated memory and provide it to other objects.
In order to clean this dead reference, Java has provided a tool called garbage collector. This garbage collector then collects all the orphan objects and frees up the memory that JVM can reuse.
Managing heap space is the most important aspect of memory optimization. Hence, first and foremost, we need to make sure we allocate enough heap space to our JVM.
Do not rely on the default heap space allocation as a part of memory optimization also involves making sure there is a proper estimation of the number of objects and their sizes. Depending on this, you can then allocate more memory using JVM options
What is Java Stack?
Stack is the home of local variables and method invocations. Stack manages order as Last-In-First-Out (LIFO). Let’s see how it works.
Your code calls a method, then its stack frame gets created and it gets put onto the top of the call stack. This stack frame maintains the state of the method. It also keeps track of the line of code currently getting executed and the values of all the local variables.
For the method on stack, it also stores references to other objects in the heap. Once the method execution ends, the stack block gets claimed and becomes available for a new method.
Compared to heap, stack has less memory allocated. As you may have figured out by now, the top of the stack contains the currently running method. Each thread has its own call stack.