Javap输出:差异静态{}和公共{}

Sve*_*ven 8 java jvm bytecode bytecode-manipulation jvm-bytecode

我有两个示例类文件,一个来自示例Java应用程序,另一个来自示例C app(使用LLJVM编译为字节码).

看看他们的输出,我可以通过javap -c -p看到,为了初始化(静态)字段,Java应用程序显示以下块:

static {};
Code: 
0: sipush 1339
3: putstatic   #7     //Field SRV_ID
etc
Run Code Online (Sandbox Code Playgroud)

<clinit>如果我理解的话,基本上就是这个方法.或者由我正在使用的VM检测到.

然而,C-app有这个:

public {};
Code: 
0: sipush 1339
3: putstatic   #7     //Field SRV_ID
etc
Run Code Online (Sandbox Code Playgroud)

这是什么?我的VM没有检测到它.

示例类文件.第一个来自Java应用程序打印消息并等待20秒,重复.第二个是大致相同的C应用程序.

http://www.fast-files.com/getfile.aspx?file=156962

http://www.fast-files.com/getfile.aspx?file=156961

这样做的道歉 - 我不会立即知道如何附加文件或有效地显示.class文件.

Hol*_*ger 5

这似乎是一个javap没有考虑的非标准声明.通常,static初始化程序被编译为名为<clinit>具有static修饰符的字节码方法.显然,javap只需打印修饰符的人类可读形式并省略<clinit>名称即可解码它们.

在这里,它遇到了一个名为<clinit>并具有public修饰符(无static修饰符)的方法,并且只是像往常一样,打印修饰符并省略<clinit>名称.

LLJVM生成的代码似乎依赖于一个古老的怪异:

class版本号为51.0或更高版本的文件中,该方法必须另外设置其ACC_STATIC标志(第4.6节),以便成为类或接口初始化方法.

此要求是在Java SE 7中引入的.在版本号为50.0或更低版本的类文件中,名为<clinit>void且不带参数的方法被认为是类或接口初始化方法,无论其ACC_STATIC标志的设置如何.

对我来说,真正令人惊讶的是,在以前的版本中,ACC_STATIC修饰符并未强制执行,我认为没有任何理由可以利用这种奇怪性.类初始化器(static {}在Java中声明)应该具有ACC_STATIC标志并且我甚至无法想象省略ACC_STATIC标志的假定语义似乎是很自然的.它意味着应该发生两个奇怪的事情之一,a)尽管没有ACC_STATIC标志(如果有的话被调用),或者b)使用必须已经"神奇地"创建的实例调用它,它在没有实例的情况下被调用. .

规范指出下列任何非标准<clinit>方法:

命名其他方法<clinit>class文件中都没有结果.它们不是类或接口初始化方法.它们不能被任何Java虚拟机指令调用,并且永远不会被Java虚拟机本身调用.

  • 这是在JDK 7中修复的JVM错误[JDK-6845426](https://bugs.openjdk.java.net/browse/JDK-6845426).我不认为lljvm故意利用它,可能他们只是忘了添加ACC_STATIC,只要JVM接受了这样的类,就没有注意到这个问题.似乎lljvm开发在2010年停止,甚至在Java 7发布之前. (4认同)