Unit 1 - Stack & Heap Memory in Java
Memory Allocation: Stack and Heap
In Java, memory allocation for variables occurs in two main areas: the stack and the heap.
Stack Memory
Stack memory is a region of memory that stores temporary variables created by each function (including the main function). It is managed by the execution stack and follows a last-in-first-out (LIFO) order.
- Stores primitive data types and references to objects.
- Memory is allocated in a last-in-first-out (LIFO) manner.
- Each thread has its own stack, ensuring thread safety.
Example:
int number = 100; // Stored in the stack
Stack Variables Tips
College Board often asks questions about stack usage.
- Since primitives are always on the stack, they point directly to the content. This is best observed in a debugger.
- A reference type contains an address to the content on the stack.
- Passing a stack variable to a method creates a copy of the content of that variable.
- Changes to the content of a primitive type will not return back to the method caller; this is called pass-by-value.
- Since a reference type contains an address to the heap, the reference is copied when calling a method. This is called pass-by-reference, as data type changes are then performed according to the reference.
Heap Memory
Heap memory is a region of memory used for dynamic memory allocation. It is managed by Java’s memory management system.
- Used for storing objects and arrays.
- Shared among all threads, requiring synchronization for thread safety.
- Managed by the garbage collector, which reclaims memory when objects are no longer in use.
Example:
// Long form showing new
String message = new String("Hello");
// Short form Java performs new find the scenes
String message = "Hello";
Heap Variables Tips
- Heap variables stay alive as long as a stack variable points to them.
- By nature, all reference data types refer to an address on the stack but change content on the heap.
- Objects created in the heap are globally accessible and can be shared among multiple methods, this creates concurrency issues when programming.
- The garbage collector automatically reclaims memory from objects that are no longer referenced, helping to prevent memory leaks.
Popcorn Hack: literal vs input
A value that is directly in code is called a literal. Often developers will say this value is hard coded value.
- Literal: In source code representation of a fixed value, e.g. 17. A hard coded number.
- String Literal: In sourced code set of letters within quotes, e.g. “blue”, A hard coded string.
Q1: Define some literal data.
// Hard code literal values
int literalAge = 17;
String literalFavoriteColor = "blue";
Q2: Obtain that data from input versus hard coded.
// Input your age
Scanner scanObj = new Scanner(System.in); // Create a Scanner object
System.out.println("Enter age");
int inputAge = scanObj.nextInt(); // Read user input
System.out.println("My Age is: " + inputAge); // Output user input
Enter age
My Age is: 16
Popcorn Hack: pass-by-value, pass-by-reference
For College Articulation in Data Structures and College Board AP Exam you will need to understand pass-by-value and pass-br-reference.
- If you pass primitives to a method they WILL NOT change the callers value.
- If you wrap the primitive in a refrence type, in the example below using a class, then you can change the original.
Q1: Describe approache difference between IntByValue and IntByReference.
public class IntByValue {
// passes int n into the function
// uses pass-by-value so when you change n in the function, it exists only in that scope
// exiting the scope (returning to main ie: changeInt() finished) --> changed n is destroyed
public static void changeInt(int n) {
System.out.println("In changeInt method");
System.out.println("\tBefore n += 10: n = " + n); // prints 5
n = n += 10;
System.out.println("\tAfter n += 10: n = " + n); // prints 10
}
public static void main(String[] args) {
int n = 5;
System.out.println("Main method before changeInt(n): n = " + n); // prints 5
changeInt(n);
System.out.println("Main method after changeInt(n): n = " + n); // still prints 5
}
}
IntByValue.main(null);
Main method before changeInt(n): n = 5
In changeInt method
Before n += 10: n = 5
After n += 10: n = 15
Main method after changeInt(n): n = 5
Q2: Try to make a changeInt method that change would persist after it is called. Be careful, this will require a change in approach.
public class IntByReference {
// passes int n into the function
// uses pass-by-reference so when you change n in the function, it changes the one in main
// similar to giving the pointer value
// exiting the scope (returning to main ie: changeInt() finished) --> changed n remains
private int value;
public IntByReference(Integer value) {
this.value = value;
}
public String toString() {
return (String.format("%d", this.value));
}
public void swapToLowHighOrder(IntByReference i) {
if (this.value > i.value) {
int tmp = this.value;
this.value = i.value;
i.value = tmp;
}
}
public static void swapper(int n0, int n1) {
IntByReference a = new IntByReference(n0);
IntByReference b = new IntByReference(n1);
System.out.println("Before: " + a + " " + b);
a.swapToLowHighOrder(b); // conditionally build swap method to change values of a, b
System.out.println("After: " + a + " " + b);
System.out.println();
}
public static void main(String[] ags) {
IntByReference.swapper(21, 16);
IntByReference.swapper(16, 21);
IntByReference.swapper(16, -1);
}
}
IntByReference.main(null);
Before: 21 16
After: 16 21
Before: 16 21
After: 16 21
Before: 16 -1
After: -1 16