Introduction
In order to understand how variables are allocated in Java and how the JVM manages the main memory, we need to understand Java’s data types and how the memory model plays into that.
Primitive data types vs non-primitive data types
Java has primitive or value data types and non-primitive or reference data types. Primitive/value data types are a set of basic data types that are predefined by the Java language. There are eight primitive data types in Java: byte
, short
, int
, long
, float
, double
, boolean
and char
. For example, the variable x in int x = 20;
is considered a primitive/value data type. In memory, the actual “value” of any variable of a primitive data type is the actual value for primitive types (20 in the previous example).
Non-primitive or reference data types contain the address or reference of dynamically created objects (e.g., clsses, interfaces, enums, arrays and immutable strings).
For example, the variable c in Car c = new Car();
is considered a non-primitive/reference data type. In memory, the actual “value” of any variable of a non-primitive/reference data type is the the reference for the object.
In Java, the new
keyword is used to instantiate an instance of a class by allocating memory for it and returning a reference to that memory location.
Memory Allocations
Before discussing the Java memory model, we need to discuss two different memory allocations: stack and heap memory. Both the stack and heap are memory areas stored in the RAM (Random Access Memory) and allocated by the operating system (OS). The heap size is determined by the OS but can grow as needed.
Stack
The stack is a region in memory where data are added and removed in a last-in-first-out (LIFO) order. When a function is executed, all local variables in the current active function are stored in the stack. The stack is considered fast with an efficient and easy access pattern to allocate and deallocate memory space. The stack, however, is a limited contiguous size memory determined when the program starts. Thus, a program may exceed the stack bound by allocating very large space for local variables or have an infinite deep recursion, which often results in a stack overflow condition and unrecoverable crash of the program. Each program routine or method has its own stack area.
Heap
The heap is another region in memory of variable size for allocating data. Unlike stack memory, heap memory is allocated explicitly by programmers (using the new
keyword in Java) and the process of deallocating memory or marking it as free is handled by the garbage collector.
When we use the new
keyword in a method, the reference (an integer) is created in the stack, but the object itself and all of its content (primitive and non-primitive/reference types) are created in the heap. In Java, objects may contain references to other objects, so the object’s content may in fact hold references to other nested objects.
Let’s take a look at this example that illustrated the use of stack and heap memory:
How reference data types are stored in memory?
In Java, all objects are dynamically allocated on heap. When we declare a variable of non-primitive type (e.g., a variable of a class type as in Course c
), only a reference is created on the stack and no memory is allocated for it yet. To allocate a memory to the object, we use the new
keyword, which will allocate memory for it on the heap. Unlike C and C++, Java does not require the manual destruction of variables allocated on the heap.
Is Java Pass-by-value or pass-by-reference?
Pass by value: means we make a copy of the parameter’s value or content.
Pass by reference means we make a copy of the address (or reference) to the parameter rather than the value or content itself.
Java is NOT a pass-by-reference language. Instead, “Java passes the reference by value.” When a variable is passed to a method, the value of that variable on the stack is copied into a new variable inside the method being called. As previously mentioned, for primitive data types, the value of that variable on the stack is the value itself. For non-primitive data types, the reference or address is stored in the stack which points to a location on the heap. When a variable is passed to a method, the value of the variable on the stack is copied into a new variable inside the new method.
Example: Consider the following Java class:
|
|
|
|
CPIT 252
CPIT 405
CPIT 405
CPIT 490
CPIT 490