Why java bytecode from a class have come code for new staic inner class appear jvm instruction ACONST_NULL
Asked Answered
D

1

3

I try to new a inner staic class, But I find that bytecode appear the jvm instruction ACONST_NULL bwteen NEW,DUP and INVOKE_SPECIAL, But I know about a class new is

  • NEW
  • DUP
  • INVOKE_SPECIAL
package com.hoho.api;

/**
 * @author linuxea
 */
public class Main {

    private static class InnerMain {
        // no field
    }

    public static void main(String[] args) {
        InnerMain innerMain = new InnerMain();
    }

}

// class version 52.0 (52)
// access flags 0x21
public class com/hoho/api/Main {

  // compiled from: Main.java
  // access flags 0xA
  private static INNERCLASS com/hoho/api/Main$InnerMain com/hoho/api/Main InnerMain
  // access flags 0x1008
  static synthetic INNERCLASS com/hoho/api/Main$1 null null

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 6 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/hoho/api/Main; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x9
  public static main([Ljava/lang/String;)V
    // parameter  args
   L0
    LINENUMBER 13 L0
    NEW com/hoho/api/Main$InnerMain
    DUP
    ACONST_NULL
    INVOKESPECIAL com/hoho/api/Main$InnerMain.<init> (Lcom/hoho/api/Main$1;)V
    ASTORE 1
   L1
    LINENUMBER 14 L1
    RETURN
   L2
    LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
    LOCALVARIABLE innerMain Lcom/hoho/api/Main$InnerMain; L1 L2 1
    MAXSTACK = 3
    MAXLOCALS = 2
}

Why

 L0
    LINENUMBER 13 L0
    NEW com/hoho/api/Main$InnerMain
    DUP
    ACONST_NULL
    INVOKESPECIAL com/hoho/api/Main$InnerMain.<init> (Lcom/hoho/api/Main$1;)V
    ASTORE 1

What is the ACONST_NULL?

Droshky answered 28/9, 2020 at 2:8 Comment(2)
Private nested classes are placed in different .class files, but classes in the same top-level context have to have a way to interact with each other; this is the reason for your synthetic inner class. The compiler creates hidden special-purpose members to allow for communication that would otherwise be forbidden by access rules. I suspect that the null comes from the fact that the nested class is static (and thus has no Outer.this). Try removing the static modifier from the nested class and see what you get.Alabaster
Mind that you can accept an answer.Buckie
R
7

This is the way how javac solves private member access problem before JDK 11.

InnerMain is declared private, and it has private default constructor:

private InnerMain() {
}

According to the JVM specification, no class may access private members of other classes, but according to Java language rules, Main should be able to access private members of InnerMain. To solve this problem, javac generates a synthetic package private bridge constructor. To ensure this constructor is not called directly from user code, compiler adds a dummy class Main$1 in the signature:

// Real constructor
private InnerMain() {
}


// Synthetic bridge constructor (package private, so Main can call it)
InnerMain(Main$1 dummy) {
    this();
}

When you write new InnerMain(), the compiler actually calls this bridge constructor. The actual value of the dummy argument does not matter, so it's just set to null - hence ACONST_NULL instruction:

public static void main(String[] args) {
    InnerMain innerMain = new InnerMain(null);
}

Note that bridge methods are not needed when the inner class is public or package private.

Since JDK 11, a fundamentally new mechanism was introduced, see JEP 181: Nest-Based Access Control. Now, if you compile your Main class with JDK 11 or newer, Main and InnerMain will become nestmates and will be able to access private members of each other without bridge methods and synthetic classes.

Riband answered 28/9, 2020 at 9:11 Comment(5)
Yes, when I make the visibility of the inner class default constructor higher than private, the synthetic bridge constructor disappears.Droshky
Emmm.I get a another question. ' Main should be able to access private members of InnerMain. To solve this problem, javac generates a synthetic package private bridge constructor. ' Why a bridge constructor can make Main be able to access private members of InnerMain?Droshky
@Droshky Because Main accesses package private bridge constructor instead of the private original constructor. And the bridge constructor in turn delegates to the original private constructor of the same class.Riband
But generating an entire class just for the sake of having a dummy parameter to tell the the original constructor and the synthetic one apart, is quite heavy. I think, even if it is never instantiated (hence not initialized), it has to be loaded to be able to invoke a constructor having it as parameter type. Eclipse just used the inner class itself for the dummy parameter instead of generating another one and it also works.Buckie
But in either case, adding a dummy parameter can have funny consequences (when maxing out the number of parameters); a formerly correctly accepted private constructor may get rejected when we add a caller from a nested class, because now it has “too many parameters”. Whereas removing the private modifier makes the compiler error go away. Thankfully, that’s past now.Buckie

© 2022 - 2024 — McMap. All rights reserved.