Q: in Java is variable memory space allocated dynamically or statically? If we run this line of code
long num = 5;

Does this variable have 64 bits of space? Or is the size determined by the value put in side? Logically it should be 64 bits as primitive datatypes are mutable. But is my conclusion true?
Furthermore, if we have an immutable object such as a string
String str = "Hello World";

Giving this String the maximum memory by default seems counter intuitive. If you were to set a String and then change it you would just change the pointer into the memory address of the second String, the newly created one.
Could someone please answer this question or point me in the right direction?

A: long is a 64-bit primitive value, regardless of what actual value you keep inside.
String is a class that wraps a char[]. The internal array has different sizes depending on the amount of characters inside. 16-bits¹ per char.
¹ It's a bit more complicated than that, but generally speaking it's 16-bits per char.

A: From JLS §4.2 and §4.2.1:

The integral types are byte, short, int, and long, whose values are 8-bit, 16-bit, 32-bit and 64-bit signed two's-complement integers, respectively, and char, whose values are 16-bit unsigned integers representing UTF-16 code units (§3.1).
The floating-point types are float, whose values include the 32-bit IEEE 754 floating-point numbers, and double, whose values include the 64-bit IEEE 754 floating-point numbers.

Clearly for these primitives to be able to have minimum and maximum values, they need to be represented by a fixed amount of memory, so it cannot be variable.
Regarding String, each object is backed by a char[] (up until JDK 9), which has a fixed length.

A: There are different kind of variables, hence, there is not a single answer.
Heap variables, also known as fields, usually have a size matching the data type, sometimes even more, depending on alignment considerations, but always enough to be able to hold any valid value of the data type. As you already mentioned yourself, a reference type points to an object in the heap memory and assigning it another reference just changes the address, so a variable of a reference type has a fixed size being able to store a heap memory address¹, regardless of the size of the referenced object. Since String instances are immutable, reassigning is the only way to change a String variable.
For local variables, the picture is different. Here, processing speed is more important than reducing memory consumption. So the data types, boolean, byte, short, char, and int are all uniformly treated as int, which you already notice on source code level, as all arithmetic done on these data types (except boolean) results in an int. This transformation already happens at compile-time. A JVM could re-establish smaller storage types when optimizing the code at runtime, but that's not the usual direction. Local variables are more likely to be mapped to CPU registers, not having any storage at all. Or, if you count registers as storage, they are at least 32 bit wide, rather 64 bit on most of today's systems.
To stay with your long num = 5; example, JVMs like HotSpot use optimizations like Static single assignment form, which allows to treat subsequent reads of num as if they were using the constant 5, so they could treat it like an int due to its actual value, but usually, the effect is to replace calculations by their predictable result which eliminates the need to store the value at all.

¹ the heap doesn't have to be as large as the theoretically addressable memory, so JVMs with CompressedOOPs use only 32 bits for object references on 64 bit architectures, as long as the heap is smaller than alignment×2³², i.e. 32GiB with the default alignment.
