Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is it possible that java.lang.Object is implemented in Java?

Tags:

java

jvm

According to the Java Language Specification, java.lang.Object is the root of Java's inheritance hierarchy. Unlike C++ or Objective-C, programmers cannot specify their own root superclasses. Because of this, I figured it was impossible to actually define java.lang.Object in Java itself. To my surprise, I found that OpenJDK indeed has a concrete implementation of java.lang.Object.

I wanted to see if it was possible to compile and run my own version of java.lang.Object with JDK/JRE 1.8, so I wrote this as Object.java:

package java.lang;
public class Object {
    public static void main(String[] args) {
        System.out.println("Hello world from custom java.lang.Object!");
    }
}

It compiled just fine with javac, but if I try to execute the classfile via java -cp . java.lang.Object, the JVM errors out with this message:

Error: Main method not found in class java.lang.Object, please define the main method as:
   public static void main(String[] args)

which suggests that the JVM is using the stock java.lang.Object instead of the Object.class in the classpath.

Is it possible to define java.lang.Object in Java as an end user? What is going on under the hood? I see two possibilities:

  • The JVM loads a real Object.class file that was compiled from the OpenJDK's Object.java. If this is the case, does the compiler do something special to prevent the classfile from specifying itself as the superclass?
  • java.lang.Object's implementation is intrinsic to the JVM. If this is the case, why is there an Object.java in OpenJDK?
like image 646
Sandra Gilbert Avatar asked Jul 10 '20 17:07

Sandra Gilbert


2 Answers

You can implement java.lang.Object in Java and the actual class you’re using has been indeed created from the Object.java file that ships with the JDK.

The Java® Language Specification says in Chapter 8. Classes:

Each class except Object is an extension of (that is, a subclass of) a single existing class (§8.1.4) and may implement interfaces (§8.1.5).

So the absence of supertypes for Object is fixed in the language.

You can use the source code of your experiment and try to add an extends or implements clause and see that the compiler will reject it.

When you compile the class java.lang.Object, the resulting class file will be the only one that has no supertype. See The Java® Virtual Machine Specification, §4.1., The ClassFile Structure:

super_class

For a class, the value of the super_class item either must be zero or must be a valid index into the constant_pool table. If the value of the super_class item is nonzero, the constant_pool entry at that index must be a CONSTANT_Class_info structure representing the direct superclass of the class defined by this class file. Neither the direct superclass nor any of its superclasses may have the ACC_FINAL flag set in the access_flags item of its ClassFile structure.

If the value of the super_class item is zero, then this class file must represent the class Object, the only class or interface without a direct superclass.

For an interface, the value of the super_class item must always be a valid index into the constant_pool table. The constant_pool entry at that index must be a CONSTANT_Class_info structure representing the class Object.

So even interfaces have an entry for the superclass in the class file (pointing to Object) and the class file for java.lang.Object is the only one with a zero entry for the super class.


When you try to load your version of the Object class at runtime, you stumble across the fact that you can’t load classes of the java.lang package (or any class whose qualified name starts with java.) through the class path in general.

Prior to Java 9, you would have to set up the bootstrap class path to include your version. Starting with Java 9, the class java.lang.Object must belong to the java.base module, which is loaded in an implementation specific manner. You’d have to use the --patch-module option to inject your own version.

But you have to be careful with what you write into your own version. There are a lot of expectations by other classes and the environment and not meeting them can break it (badly).

JLS, §4.3.2. The Class Object lists the expected methods and links to other chapters that define special language semantics for some of them.

like image 197
Holger Avatar answered Oct 02 '22 13:10

Holger


You can modify java.lang.Object (e.g. by adding public static void main() method), but in order to be loaded and used by the JVM, the modified class needs to be added to the bootstrap class path.

On JDK 8 this can be done with

java -Xbootclasspath/p:<path>

On JDK 9+ this requires patching java.base module:

java --patch-module java.base=<path>

When the JVM starts, it loads java.lang.Object by the bootstrap class loader just like any other class, so java.lang.Object with the added main method can be actually executed:

$ java -Xbootclasspath/p:. java.lang.Object
Hello world from custom java.lang.Object!

However, if you try to remove existing java.lang.Object methods, add new virtual methods, add fields or otherwise change the existing layout - this won't work. Most likely, the JVM will just crash with the fatal error.

This is because the JVM expects java.lang.Object to have the known layout. There are hard-coded offsets in the JVM source code, references to the exiting methods, etc. The same is true for other intrinsic classes like java.lang.String, java.lang.Class, java.lang.ref.Reference and similar.

As to Object's superclass, there is an exception explicitly described in JVM Specification:

If the value of the super_class item is zero, then this class file must represent the class Object, the only class or interface without a direct superclass.

Both Java compiler and the JVM know about this exception, and enforce this rule when compiling Object.java and when loading Object.class.

like image 30
apangin Avatar answered Oct 02 '22 14:10

apangin