Skip to the content.
Intro Primitive Types Reference Types Stack and Heap Code Example Quiz

Unit 1 - Stack & Heap Memory

An overview of stack and 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: Litterally typed it in (Ex: a hard-coded number; 23, 19, 20) Q2: Obtain that data from input versus hard coded: Hard coded = literal; Input data is dynamic based on UI.

// Hard code literal values

int literalAge = 17;
String literalFavoriteColor = "blue";
// 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

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 the approach difference between IntByValue and IntByReference.

  • IntByValue:
    • When you pass a primitive data type (like int) to a method in Java, it is passed by value. This means that the method receives a copy of the value, and any changes made to the parameter inside the method do not affect the original variable. The original value remains unchanged after the method is called.
  • IntByReference:
    • To allow a method to modify the original value, you can wrap the primitive in a reference type, such as an object or an array. When an object reference is passed to a method, it is passed by reference. This means that the method receives a reference to the original object, and any changes made to the object’s fields inside the method will affect the original object. As a result, changes persist after the method is called.

Q2: Try to make a changeInt method that would persist after it is called. Be careful, this will require a change in approach.

To create a changeInt method where the change persists after it is called, you can wrap the int value in an object.

Explanation:

  • By wrapping the primitive int in a reference type (like a custom wrapper class), you allow the method to modify the value inside the object. Since objects are passed by reference in Java, the change made to the object’s value inside the method will persist after the method is called. This contrasts with passing a primitive directly,
public class IntByValue {
    
    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
public class IntByReference {
    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