简体   繁体   中英

Java runtime memory model --

I'm looking for verification/correction on the following.

Assume the following inheritance hierarchy -

class A {
    void m1() { System.out.print(System.currentTimeMillis()); }
    void m2() { System.out.print(new Date()); }  // current date

}

class B extends A {
    int x;
    void m1() { System.out.print(x); }  // overriding
    void m99() { System.out.print(++x); }  
}

Also assume that, class B is instantiated at some point in the application, ie the following statement is executed -

B b = new B();

(Paragraph-A) 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.

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 .

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.

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.

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.

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 .

Is this all accurate? If not, where/why not?

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(); 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.

The methods m1() , m2() and m99() all have their definitions in this space of b .

Incorrect. The space allocated for b (the instance of B ) references the class itself, ie the space allocated for class B , where the method definitions are stored.

The space allocated for an object instance consists of an object header and the data of the instance, ie the values of the fields. See eg What is in java object header for more information about the object header.

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 .

Incorrect. The bytecode for class B knows nothing about method m2() .

Remember, class A may be compiled separately from class B , so you can remove method m2 without recompiling class B .


UPDATE

From comment :

How then is it known what to execute when b.m2() is run? I don't think JRE goes to the super-class of B , looks to see an m2() there, if no such method then goes to super-super class, ... Too inefficient in runtime. Must be a direct reference to m2() . m2() is a member of B -- even though inherited.

As already stated in the answer, m2() is NOT a member of B . If you run the Java Disassembler, ie run javap B.class on the command-line, you'll see:

class B extends A {
  int x;
  B();
  void m1();
  void m99();
}

As you can see, the compiler has added the default constructor for you, but has not added any m2() method.

Now create this class:

class C {
    public static void main(String[] args) {
        B b = new B();
        b.m2();
    }
}

Then disassemble it with the -c switch, ie javap -c C.class :

class C {
  C();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #16                 // class B
       3: dup
       4: invokespecial #18                 // Method B."<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #19                 // Method B.m2:()V
      12: return
}

As you can see, the compiler generates an instruction to call B.m2() , even though we already saw that B.class doesn't know about m2() .

This means that what you postulated is exactly what happens, ie the JVM needs to resolve the method to class A at runtime, by walking up the superclass chain.

If m2() is removed from class A and recompiled, without recompiling class C , you will get NoSuchMethodError: 'void B.m2()' when running the code.

It's all visible in the .class files if you use a disassembler like javap . The bytecode for class B does not contain method m2. Further reading about Java handles inheritance here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM