简体   繁体   中英

Why InvokeVirtual used instead of InvokeSpecial when calling class.NewInstance()?

I was looking into the disassembly of the following java program

public class ASMPlayground {
    private String bar;

    public String getBar(){
        return bar;
    }

    public void setBar(String bar) throws IllegalAccessException, InstantiationException {
        String name = String.class.newInstance();
        System.out.println(name);
    }

    public static void main(String[] args) {

    }
}

The following bytecode snippet caught my eye and seemed sub-optimal

LDC Ljava/lang/String;.class
INVOKEVIRTUAL java/lang/Class.newInstance ()Ljava/lang/Object;
CHECKCAST java/lang/String

Question:

InvokeVirtual is used when the method to be executed is dependent on the object reference. Given that "Class" is final and newInstance() exists only in the "Class" why not use InvokeSpecial instead of InvokeVirtual ? Wouldn't it be more performant?

InvokeSpecial is used to designate invocations that are

superclass, private, and instance initialization method invocations

Spec: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokespecial

Class.newInstance is neither superclass method call nor private method call nor call to initialization method. It also isn't a dynamic method, InvokeDynamic has nothing to do here. Both instructions cannot be used here because it would confront jvms. At the time of creating jvms it was possible to allow substitute of some instructions with "more performant" ones, but as I see in jvms it hasn't been done.

Wouldn't it be more performant?

JIT is smart enough to understand that there's no need to traverse virtual method table in such cases, so it shouldn't slow execution down. Actual performance should be compared by actual tests, but I see no reasons to expect significant difference there.

The fact that a target method or class is final does never change the compiled form a class using the other class/calling the method.

This is mandated by the JLS, Chapter 13. “Binary Compatibility”, §13.4.17, final methods :

Changing a method that is declared final to no longer be declared final does not break compatibility with pre-existing binaries.

Likewise §13.4.2. final Classes

Changing a class that is declared final to no longer be declared final does not break compatibility with pre-existing binaries.

Obviously, an invocation instruction relying on the target method's final nature would violate this specification.

There is no relevant performance impact to expect. There is always a first time overhead, when the symbolic reference has to be resolved to the actual method (in terms of the JVM's runtime representation). Right at this point, the JVM can also record the fact that this method or its declaring class is actually final to the invocation instruction, if there is a benefit. Modern JVMs go even farther and, eg utilize the fact that a non- final method has not been actually overridden to the same effect, though these invocations would need to be deoptimized if a subclass gets loaded and instantiated, which has an overriding method. So the only difference is that a final modifier guarantees that such deoptimization will never be necessary.

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