To add to the other answer for parts 1&2 only of the question:
Currently, the JVM uses run-time data areas that can be divided into six areas:
• The program counter (PC) register
• Java Virtual Machine (JVM) stacks
• Native method stacks
• Heap Area
• Method area
• Run-Time Constant Pool
The PC register is used to store the address of the next instruction, which is the instruction code to be executed. The execution engine reads the next instruction and the JVM uses this to keep track of the executions of threads, because the CPU will constantly switch between them.
The stack frame has three parts: the Local Variable Array, the Operand Stack and the Frame Data.
- The purpose of the Operand Stack is for any intermediate operations that may be required, such as addition or subtraction of numbers. The operand stack acts as runtime workspace to perform the operation.
- The Local Variable Array contains all parameters and local variables of the method.
- Frame Data: contains all symbolic references (constant pool resolution) and normal method returns related to that particular method.
Native Stacks are used when an implementation of the Java Virtual Machine is using conventional stacks, colloquially called "C stacks," to support native methods (methods written in a language other than the Java programming language).
The heap is the run-time data area from which memory for all class instances and arrays is allocated.
The Java Virtual Machine has a method area that is shared among all Java Virtual Machine threads. The method area is analogous to the storage area for compiled code of a conventional language or analogous to the "text" segment in an operating system process. It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods used in class and interface initialization and in instance initialization (§2.9).
A run-time constant pool is a per-class or per-interface run-time
representation of the constant_pool table in a class file (§4.4)
So what does that mean in practical terms? My understanding is that it is an abstraction to reference classes without any side effects.
For example, what if you were using the Class object instead of the constant pool symbols for storing class structures at runtime and it didn't load properly? I've seen a code base with multiple jars of the same name, type, and version all loaded from different areas of the code, because multiple teams worked on separate sections of a large code base and didn't bother to clean up the mess. I also worked with tuples of strings as a means of constructing a unique location in a transportation program. It worked, but it seemed to be a clunky mechanism for handling the use case. So my point is, the symbolic representations for classes and interfaces fill that need and create a standard procedure to reference classes during runtime, without side effects, to my knowledge.
To quote Brian Goetz on this:
Activities such as bytecode generation have a frequent need to
describe constants such as classes. However, a Class object is a poor
description for an arbitrary class. Producing a Class instance has
many environmental dependencies and failure modes; loading may fail in
because the desired class does not exist or may not be accessible to
the requestor, the result of loading varies with class loading
context, loading classes has side-effects, and sometimes may not be
possible at all (such as when the classes being described do not yet
exist or are otherwise not loadable, as in during compilation of those
same classes, or during jlink-time transformation.) So, while the
String class is a fine description for a Constant_String_info, the
Class type is not a very good description for a Constant_Class_info.
A number of activities share the need to deal with classes, methods, and
other entities in a purely nominal form. Bytecode parsing and
generation libraries must describe classes and method handles in
symbolic form. Without an official mechanism, they must resort to
ad-hoc mechanisms, whether descriptor types like ASM's Handle, or
tuples of strings (method owner, method name, method descriptor), or
ad-hoc (and error-prone) encodings of these into a single string.
Bootstraps for invokedynamic that operate by spinning bytecode (such
as LambdaMetafactory) would prefer to work in a symbolic domain rather
than with live classes and method handles. Compilers and offline
transformers (such as jlink plugins) need to describe classes and
members for classes that cannot be loaded into the running VM.
Compiler plugins (such as annotation processors) similarly need to
describe program elements in symbolic terms. They would all benefit
from having a single, official way to describe such constants.
The Java docs quoted and elaborated on were from Java SE 12.
There are diagrams to illustrate this: the JVM Architecture from Dzone.
Last of all is An Introduction to the Constant Pool in the JVM by Baeldung that illustrates a simple Hello World program with bytecode.