8

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

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

static {};
Code: 
0: sipush 1339
3: putstatic   #7     //Field SRV_ID
etc

<clinit>如果我理解的话,这基本上是方法。或者被我正在使用的虚拟机检测到。

然而,C-app 有这个:

public {};
Code: 
0: sipush 1339
3: putstatic   #7     //Field SRV_ID
etc

这是什么?我的虚拟机没有检测到它。

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

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

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

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

4

1 回答 1

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 虚拟机本身调用。

于 2018-03-14T09:37:58.460 回答