Memory can be described as an array of bytes where you can access each byte individually. In each byte or rather, location, in the memory, there is data that you can access, just like in arrays in Java. In 32-bit architectures, each memory “slot” contains 32 bits, or also called 1 word, or just 4 bytes.
In the representation above, 4000, 4004, 4008, etc. represent the address of the memory slot or in the comparison I did with the arrays, the indices of the data, or the memory locations. Each of these “random value”s represent 32 bit. Since each memory slot takes 32 bits, or 4 bytes, the memory addresses increment by 4 every time.
Usually, in Java and in programming in general, there are two types of variable scope – global and local. Global variables are those that can be accessed from everywhere in the program whereas the local variables are the ones that can be only accessed within a given function, where they have been created. These two different types of variable scope, therefore, are stored in different memory regions – the Stack and the Data.
Stack
public void doSomething() { int v = 0; System.out.println(v); }
In the simple Java example above, v is stored in the Stack memory region. It is because v is a local variable.
Static data
public class Example { int globalVar = 3; public int showVar() { return globalVar; } }
In the Java example above, globalVar is in the static data memory region. Because as you can see, it can be accessed from the method, even though it’s not created within it. In addition, it can be accessed throughout the entirety of the program.
Heap
public class Person { int pid; String name; public Person(int id, String name) { this.pid = id; this.name = name; } }
public class Driver { public static void main(String[] args) { int id = 1; String pName = "Rick"; Person p = new Person(id, pName); } }
In the Java example above, where we create a new instance of the Person class and store it in “p”, essentially we are using the Heap region to dynamically allocate us the memory that we need to create such an instance using the new keyword. In other words, it is not fixed to a certain size. However many bytes the instance is, if there is enough memory (there will be likely), the instance will be created and it will only reserve the amount of bytes necessary to create it.
Java saves us a lot of trouble when it comes to dynamic memory allocation, because in some other languages, like C for example, you have to manually allocate the memory and manually “free” the same memory when you won’t need it anymore, whereas in Java, everything is happening behind the scenes, simply by calling the keyword new.
I will suggest following tutorial on how to adjust the JVM to use specific amount of memory