Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Memory Allocation and Wrapper Class Caching in Java

Tech 1

Java manages memory across several distinct regions, with the stack and heap being the most critical for variable and object storage. Primitive types (byte, short, int, long, float, double, char, boolean) retain their raw values directly, while reference types hold pointers to actual objects in memory.

Stack vs. Heap Memory

The stack is used for storing primitive data types and object references. It operates with a fast, Last-In-First-Out (LIFO) structure, and memory allocation/deallocation is handled automatically when variables enter and exit their scope. The size and lifecycle of stack data must be deterministic.

Conversely, the heap is dedicated to dynamically allocated memory, specifically objects and arrays instantiated via the new keyword. The JVM's garbage collector manages heap memory, reclaiming space only when objects no longer have active references. An object resides in the heap while its reference variable lives on the stack.

Primitives and Literal Sharing

Primitives are not object instances; they are pure literal values. Because stack data can be shared, declaring int x = 5; followed by int y = 5; results in both variables pointing to the same memory address holding the literal 5. The compiler reuses existing literal values on the stack to optimize memory.

Wrapper Classes and Autoboxing

Since primitives lack object-oriented features, Java provides wrapper classes (e.g., Integer, Double) to encapsulate them. The conversion from a primitive to its corresponding wrapper is called boxing, while the reverse is unboxing.

Since JDK 5, Java supports autoboxing and auto-unboxing:

Integer boxed = 42;              // Autoboxing: compiles to Integer.valueOf(42)
int unboxed = boxed;             // Auto-unboxing: compiles to boxed.intValue()

When autoboxing occurs, the compiler invokes the valueOf method rather than calling the constructor directly. This distinction is crucial due to the caching mechanism employed by certain wrapper classes.

The Integer Cache Mechanism

The Integer.valueOf(int) method leverages a caching pool to conserve memory and improve performance. Instead of always creating a new object, it returns a cached instance for values within a specific range.

public static Integer valueOf(int num) {
    if (num >= IntegerPool.MIN_VAL && num <= IntegerPool.MAX_VAL)
        return IntegerPool.pooledInstances[num - IntegerPool.MIN_VAL];
    return new Integer(num);
}

The IntegerPool (internally IntegerCache) pre-instantiates Integer objects for the range -128 to 127 (by default, though the upper bound can be configured via JVM arguments).

private static class IntegerPool {
    static final int MIN_VAL = -128;
    static final int MAX_VAL;
    static final Integer[] pooledInstances;

    static {
        int upperBound = 127;
        String customMax = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (customMax != null) {
            upperBound = Math.max(parseInt(customMax), 127);
            upperBound = Math.min(upperBound, Integer.MAX_VALUE + MIN_VAL - 1);
        }
        MAX_VAL = upperBound;
        pooledInstances = new Integer[MAX_VAL - MIN_VAL + 1];
        int current = MIN_VAL;
        for (int idx = 0; idx < pooledInstances.length; idx++) {
            pooledInstances[idx] = new Integer(current++);
        }
    }
}

Equality Implications of Caching

Because of this cache, comparing wrapped values using == can yield different results depending on the value's range.

public class WrapperEvaluation {
    public static void main(String[] args) {
        Integer obj1 = new Integer(12);
        Integer obj2 = 12;
        Integer obj3 = Integer.valueOf(12);

        Integer obj4 = new Integer(232);
        Integer obj5 = 232;
        Integer obj6 = 232;

        Double dblObj = 232.0;

        System.out.println(obj1 == 12);   // true (unboxing occurs)
        System.out.println(obj1 == obj2); // false (different references: heap vs cache)
        System.out.println(obj2 == obj3); // true (both reference the same cached object)

        System.out.println(obj4 == 232);  // true (unboxing occurs)
        System.out.println(obj4 == obj5); // false (different heap references)
        System.out.println(obj5 == obj6); // false (outside cache, distinct heap objects)

        System.out.println(obj5.equals(obj6)); // true (values are equal)
        System.out.println(obj5.equals(dblObj)); // false (different types)
    }
}

The equals method in Integer checks if the compared object is also an Integer before comparing the primitive values.

Caching Behavior in Other Wrapper Types

Floating-point types like Double and Float do not implement a cache because the number of possible fractional values within any range is infinite.

Double d1 = 100.0;
Double d2 = 100.0;
Double d3 = 200.0;
Double d4 = 200.0;

System.out.println(d1 == d2); // false
System.out.println(d3 == d4); // false

Other wrapper types maintain specific caches:

  • Byte & Boolean: Cache all possible values.
  • Short & Long: Cache values from -128 to 127.
  • Character: Cache values from 0 to 127.

For Boolean, the valueOf method returns one of two static final instances, ensuring safe rfeerence equality.

Boolean flag1 = false;
Boolean flag2 = false;
Boolean flag3 = true;
Boolean flag4 = true;

System.out.println(flag1 == flag2); // true
System.out.println(flag3 == flag4); // true

This behavior is driven by the internal implementation of Boolean.valueOf:

public static Boolean valueOf(boolean val) {
    return val ? TRUE : FALSE;
}

public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.