dock with containers

The last thing I want to discuss about integral primitives is what happens when we use them as fields in our classes. A typical example looks like this:

I created similar classes for short, byte and long and I uploaded them on github. I know how much memory java needs to represent one field of each primitive type but I want to know what really happens in backstage when objects are allocated. I found a good post about the memory structure of a java object and relevant information is:

  • Every object has a header and member fields
  • Every object is 8 byte aligned in memory
  • In 64 bit JVM with heap less than 32 Gb
    • Header has 12 bytes
    • Array header has 16 bytes. It needs 4 extra bytes for length.
    • A reference is 4 bytes by default.

Based on the above information I can calculate the real memory consumption of prepared sample objects but I would prefer to have a tool that does this for me. In this way I can double check if my understanding is right. I found jamn which provides a class, MemoryMeter, that can be used for this job:

After running this competition the results are:

Without knowing the background of memory allocation I would be really surprised that the first three objects take the same amount of memory but the explanation is quite simple:

  • ByteField needs 12 bytes (header) + 1 byte (byte) + 3 bytes (8 byte alignment)
  • ShortField needs 12 bytes (header) + 2 bytes (short) + 2 bytes (8 byte alignment)
  • IntField needs 12 bytes (header) + 4 bytes (int)
  • LongField needs 12 bytes (header) + 8 bytes (long) + 4 bytes (8 byte alignment)

My conclusion after checking real memory allocation is that we need to measure what really happens in the background and not sacrifice the semantics of our application for a potential optimization.

In first post on primitives competition we found that most of the operations performed similarly except / and % on long type and last relatively complex expression that included all other operations. In second post I concluded that for local variables there isn’t a real difference between byte, short and int. All instances occupy the same space (4 bytes) and the operations are the same.

Based on all these conclusions there is no real difference between byte, short and int when used in mentioned situations. So why should we care about them? According to primitive data types tutorial we should use these types like below:

  • byte: The byte data type can be useful for saving memory in large arrays, where the memory savings actually matters. They can also be used in place of int where their limits help to clarify your code; the fact that a variable’s range is limited can serve as a form of documentation.
  • short: As with byte, the same guidelines apply: you can use a short to save memory in large arrays, in situations where the memory savings actually matters.
  • int: Use the Integer class to use int data type as an unsigned integer.
  • long: Use this data type when you need a range of values wider than those provided by int.

The advice is to use byte and short in large arrays. It’s time for a new competition to check it. I prepared test classes like this:

I created similar classes for short, byte and long and I uploaded them on github. I used jamn MemoryMeter class:

and when array is created with 1000 elements the results are:

  • ByteArray needs 12 bytes (header) + 4 bytes (array reference) + 16 bytes (array header) + 1000 bytes (byte elements)
  • ShortArray needs 12 bytes (header) + 4 bytes (array reference) + 16 bytes (array header) + 2000 bytes (short elements)
  • IntArray needs 12 bytes (header) + 4 bytes (array reference) + 16 bytes (array header) + 4000 bytes (int elements)
  • LongArray needs 12 bytes (header) + 4 bytes (array reference) + 16 bytes (array header) + 8000 bytes (long elements)

Now the difference between these primitives is very clear. You could say that all I had to do is to read primitive data types tutorial. It is true, but I had no idea what the meaning of where the memory savings actually matters is.

Environment:
java version “1.8.0_66”
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)

Image credit: Jarosław Bialik, CC0 Public Domain