Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does primitive data type consume much memory than reference data type for this scenario in Java?

I tried to check the memory consumption of a program. During the check, I have noticed some interesting things.

I created a Load class, which contains some fields.

class Load {
    String name;
    String title;
    long id;
}

I created 500000 Load objects and add them to an ArrayList. I have found, it took around 18 MB of memory.

Then, I modified the Load class and use reference type Long.

class Load {
    String name;
    String title;
    Long id;
}

Again created 500000 Load objects and add them to ArrayList. Interestingly this time it took less memory than the previous one. It way 14 MB.

Run test changing os and JVM version. Found the following results.

OS: Windows 10 Pro 64 bit
JDK: 11 64bit
 
Object Created  | Load Object         | Memory | Load Object         | Memory  
------------------------------------------------------------------------------
1. 500000       | With primitive long | 18 MB  | With reference Long | 14 MB
2. 900000       |                     | 32 MB  |                     | 26 MB
3. 1500000      |                     | 53 MB  |                     | 41 MB

OS: macOS Big Sur 64 bit
JDK: 8 64bit
 
Object Created  | Load Object         | Memory | Load Object         | Memory  
------------------------------------------------------------------------------
1. 500000       | With primitive long | 18 MB  | With reference Long | 14 MB
2. 900000       |                     | 32 MB  |                     | 26 MB
3. 1500000      |                     | 53 MB  |                     | 41 MB

Surprisingly, in all of these test runs, Object contains primitive types long consume more memory than Object contains reference Long.

My question is, why primitive type takes more memory in this scenario?

Memory Tester Code:

public class MemoryChecker {

    private static final long MEGABYTE = 1024L * 1024L;

    public static long bytesToMegabytes(long bytes) {
        return bytes / MEGABYTE;
    }

    public static void main(String[] args) {
        List<Load> list = new ArrayList<Load>();
        for (int i = 0; i <= 500000
                ; i++) {
            list.add(new Load("Jim", "Knopf", 11L));
        }
        // Get the Java runtime
        Runtime runtime = Runtime.getRuntime();
        // Run the garbage collector
        runtime.gc();
        // Calculate the used memory
        long memory = runtime.totalMemory() - runtime.freeMemory();
        System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory));
    }
}

Complete code git repo.

like image 880
Md Kawser Habib Avatar asked Apr 29 '26 10:04

Md Kawser Habib


2 Answers

For 32 Bit JVMs, but also for 64 Bit JVMs with the CompressedOOPs feature (which is supported by the HotSpot JVM and on by default), a reference consumes only 4 bytes, compared to the 8 bytes of a long.

Even when you initialize the reference with an actual object, it may consume less memory when the object is shared. This applies to autoboxing of constants:

If the value p being boxed is the result of evaluating a constant expression (§15.29) of type boolean, byte, char, short, int, or long, and the result is true, false, a character in the range '\u0000' to '\u007f' inclusive, or an integer in the range -128 to 127 inclusive, then let a and b be the results of any two boxing conversions of p. It is always the case that a == b.

but also to all operations ending up at Long.valueOf(long) in general.

This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.

Of course, if you create a lot of unshared Long objects, they will consume far more memory than the primitive long. If you use a lot of distinct values, even a potential sharing of them wouldn’t help.

like image 84
Holger Avatar answered May 01 '26 03:05

Holger


  • The largest primitive can hold 8 bytes.

  • every Object has at least 12 bytes (with default 64 bit VMs and relatively small heap) of headers. Automatically making it bigger than a primitive.

There is a single good library that I am aware of that does correctly, called jol. here is a related question.

It is rather easy to get jol set-up and run the samples to understand the actual numbers you are interested in.

like image 26
Eugene Avatar answered May 01 '26 03:05

Eugene



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!