Generally, execution environments for Java can get implemented in various ways and it is impossible to say what “Java” does in general.
When the application is built, both classes A
and B
are loaded into memory
by the static loader.
Both classes are in memory, along with the definitions of all their member methods.
The standard way of deployment is to compile Java source code to bytecode. When the application is executed, the classes will be loaded. There is no such thing as a “static loader”. There are different class loaders. When the class files are delivered on the class path, they will be loaded by the application class loader.
When B
is instantiated with the above statement, a memory space in the heap
is allocated for that object b
.
The methods m1()
, m2()
and m99()
all have their definitions in this space of b
.
As said by Andreas, the method definitions are part of the JVM’s class representation. The object only contains a reference (pointer) to the class.
These method definitions are only references to the method "templates" existing
in the class definitions. In the class, these methods are sequences of operations--
parameterized operations if the method is executing on any parameter and/or global variable.
The terms “definitions” and “templates” and the way you use them, are creating unnecessary confusion. The instruction sequences are part of a methods definition. There is a reference from the object to these definitions, either indirectly via the already mentioned reference to the class, or directly via a table of method pointers, known as “vtable”, a widespread optimization.
When one of the methods, say b.m99()
is invoked at runtime, JRE goes to class definition of B
to get that "template" (sequence of operations), looks up the current values of the fields of b
, fills in that "template" with the current values of these field(s), also pushes these current values to the stackspace and runs the methods by executing these operations it found on the class definition.
You should forget about that term “template”. The method definition contains a sequence of executable instructions and the JVM will execute these instructions. For instance methods, a pointer to the object data becomes the implicit first argument. No template will be filled with anything.
If the method is inherited from a superclass, Eg. m2()
above, the definition of that
method in the class (the definition mentioned in Paragraph-A above) is itself a reference to the definition of m2()
in class A.
At runtime, when b.m2()
is executed, JRE goes directly to class A
to find that "template" for the low-level operations to execute.
This is an implementation detail, but hold your breath…
These references to method definitions are checked at compile time and put into the bytecode. Eg. in the bytecode for the above case, class B
has, a direct reference to method m2()
of class A
for method m2()
it's inheriting from A
.
This is not, how Java works. In Java, the compiler will check for the presence of the invoked method, which succeeds as B
inherits the method from A
and its accessible, then, it will record an invocation as written in source code, for m2()
invoked on B
.
The fact that B
inherits the method, is an implementation detail of B
and allowed to change. A future version of B
may override the method. If that happens, A.m2()
may even get removed. Or a class C
may get introduced between A
and B
(C extends A
and B extends C
), which are all backwards compatible changes.
But back to the previous section, at runtime, an implementation may utilize the knowledge about actual inheritance. A JVM could search the super type hierarchy each time, a method is invoked, that would be valid but not very efficient.
A different strategy is to have the “vtable” mentioned above. Such a table is created for each class when it is initialized, starting with a copy of all superclass methods, entries of overridden methods replaced, and newly declared methods at the end.
So when an invocation instruction is executed the first time, it gets linked by determining the associated index in the vtable. Then, every invocation only needs to fetch the method pointer from the vtable of the object’s actual class, without ever traversing the class hierarchy.
That’s still only the way, an interpreted or less optimized execution works. When the JVM decides to optimize the invoking code further, it may predict the actual target of the method invocation. There are two ways in your example
the JVM uses the knowledge that A.m2()
has never overridden (it would have to remove such an optimization when a new class is loaded which does override the method)
It analyzes the code path, to determine that for B b = new B(); b.m2();
the target is fixed as the result of new B()
is always of type B
, not a superclass and not a subclass.
When the target is predicted, it can be inlined. Then, the optimized code simply does System.out.print(new Date());
and when there’s no other use of the B
instance, even the allocation may get eliminated.
So what the JVM does at runtime, may be entirely different than what has written in source code. Only the perceivable result (the date is printed) will be the same.