Java Primitives vs Wrapper : Which one to use?

Just for the reference, In java primitive type variables occupy below size in memory and they live in stack if declared as local variable and on heap if they are instance variables :
  • boolean — 1 bit * (This is representational, its size not precisely defined)
  • byte — 8 bits
  • short, char — 16 bits
  • int, float — 32 bits
  • long, double — 64 bits
Note : In Oracle’s Java Virtual Machine implementation, boolean arrays in the Java programming language are encoded as Java Virtual Machine byte arrays, using 8 bits per boolean element.

Object “housekeeping” information overhead

Before getting into granularity of object size you should be aware of the fact that different HotSpot’s have different object header sizes, it has 8-byte, 12-byte, and 16-byte object headers. Also in the most common case (-Xmx below 32GB), object header size is 12 bytes, and with bigger -Xmx it's 16 bytes.
This is because the header contains two parts namely markword (metainfo about the object) and classword (the reference to class). In 32/64-bit mode markword takes up either 4 or 8 bytes. Classword is “just” the reference, and hence it can be compressed in 64-bit mode.
In a 32bit JVM, the Shallow size of an object is
8 bytes (object header) + total of all instance variables + padding (optional)
If the first 2 terms don’t add upto a multiple of 8 there will be padding.
In a 64 bit JVM, the Shallow size is
16 bytes (object header) + total of all instance variables + padding (optional)
but with CompressedOops (compressed klass word, the mark word is not encoded) the 16 byte header can be reduced to 12 bytes.
So 4 bytes (64 bit compressed klass word) + 8 bytes (mark word) = 12 bytes (header)
Each header will keep the following information:
• Hash Code : Object's hash code.
• Garbage Collection Information : This is often one or two bits of the flag, but it can be some combination of bits to store the number of references to the other objects within the given object.
• Type Information Block Pointer : This contains information about the type of object. This block contains information about the virtual method table, a pointer to an object that represents the type and pointers to some additional structure for more efficient call interfaces, and dynamic type checking.
• Lock : This may be a pointer to an object or direct representation of the locking.
• Array Length : If the object is an array, then the header extends by 4 bytes to store the length of the array.

Object size granularity

In case of 32 bit system the header size is 8 bytes, in the case of 64-bit system, respectively is 16 bytes.
Every object occupies a number of bytes that is a multiple of 8. If the number of bytes required by an object for its header and fields is not a multiple 8, then you round up to the next multiple of 8.
This means, for example, that:
  • a bare Object takes up 8 bytes;
  • an instance of a class with a single boolean field takes up 16 bytes:
8 bytes of header, 1 byte for the boolean and 7 bytes of “padding” to make the size up to a multiple of 8;
  • an instance with eight boolean fields will also take up 16 bytes:
8 for the header, 8 for the booleans; since this is already a multiple of 8, no padding is needed;
  • an object with a two long fields, three int fields and a boolean will take up:
8 bytes for the header;
16 bytes for the 2 longs (8 each);
12 bytes for the 3 ints (4 each);
1 byte for the boolean;
a further 3 bytes of padding, 
to round the total up from 37 to 40, a multiple of 8.
So In 32-bit JDK, the overhead is 8 bytes, padded to a multiple of 4 bytes which means int primitive consumes only 4 bytes but the Integer object takes 16 bytes, we see that there is 300% memory overhead.
Quote : In Item 5, of Effective Java, Joshua Bloch says
The lesson is clear: prefer primitives to boxed primitives, and watch out for unintentional autoboxing.
For Instance, If you run the below program :
// Hideously slow program! Can you spot the object creation?
public static void main(String[] args) {
    Long sum = 0L;
    for (long i = 0; i < Integer.MAX_VALUE; i++) {
         sum += i;
    }
    System.out.println(sum);
}
This program gets the right answer, but it is much slower than it should be, due to a one-character typographical error. The variable sum is declared as a Long instead of a long, which means that the program constructs about 2^31 unnecessary Long instances (roughly one for each time the long i is added to the Long sum). Changing the declaration of sum from Long to long reduces the runtime from 43 seconds to 6.8 seconds on my machine.
Conclusion :
Use primitives in preference to boxed primitives whenever you have choice. Primitive types are simple and faster. If you must use boxed primitives, be careful! Autoboxing reduces the verbosity, but not the danger, of using boxed primitives
Source : https://www.baeldung.com/java-primitives-vs-objects

Above Image shows the performance impact, since primitives live on stack and as they are accessed faster than on heap their average execution time is pretty low in comparison to the corresponding wrapper object counter parts.
Further Reading & References: