Java字节码中的一些特定处理

本文包括的一些描述为以下列表

  • 常量池大小设定
  • stackMapTable属性
  • new指令后的dup
  • attribute属性
  • long和double的特殊处理
  • monitorenter和monitorexit的使用场景
  • enclosingMethod属性
  • localVariableTable和localVariableTypeTable
  • constantValue属性

常量池大小设定
常量池的大小的虽然为N,但实际的常量数量即为N-1,但并不是说它的下标值为0到N-1,它的下标是从1到N-1。这里面省略了下标值为0的值,虽然字节码中没有此值。我们可以认为下标为0的值即代表某一种null值。包括其它地方进行引用时,也采用的这个位置处理。如字节码中常量池大小为26,那么最后一个下标肯定为25.

stackMapTable属性
stackMapTable表示在字节码验证中的某种变量和类型和对照关系,此关系从1.6开始才有,以用于更好地进行字节码运行期验证,而不是使用原始的类型推断。因此,如果我们只生成1.5的字节码,则不需要处理此信息。这东西跟相应的堆栈表是完全没有任何关系的,只不过在字面上有点相似,在字节码相应的指令查看中,我们完全可以不关心此值,它是属于虚拟机加载验证中需要处理的信息。

new指令后的dup
new字节码后面后跟一个dup,这是因为需要有对象去调用new方法,而这个new方法是没有返回值的。而dup相应于对指针的一个复制,其实就相当于原对象。这时候,使用了一个指针来调用相应的new方法,而还有一个正好用于从堆栈中表示刚初始化好的对象信息。相当于有2个一样的指针,调用构造方法用掉一个,还剩下一个用于后面的处理。

atrribute属性
针对于方法,字段,类的额外信息描述,包括参数上的名字,本地表这些信息,甚至包括注解,执行代码,都是使用attribute来表示的。而各种不同的attribute正好用来实现不能的字节码语义。可以说,通过不同的attribute定义,可以定义不同的数据结构,同时实现新的功能信息,并且也不会由于新的attribute导致旧的JVM不能执行。包括后面新的JVM属性处理,也期望使用attribute来实现,因为这个就属于扩展属性,随意扩展而不会影响其它指令。

long和double的特殊处理
针对long和double,而有一些特殊的规则,如在堆栈中需要占2个位置,在localVariableTable中也占2个位置。进行对象复制时,也需要调用dup2和pop2,调用dup对一个long类型是不正确的。

monitorenter和monitorexit
只有在方法内部使用synchronized同步块(即针对某一个对象作同步时)才会生成monitorenter和monitorexit方法,针对方法本身的synchronized修饰是由JVM自行实现,而不是由字节码实现。如下的代码所示:

synchronized(obj) {
//code......
}

enclosingMethod属性
属性表中的EnclosingMethod表示当前的一个内部类是在哪一个方法中被定义的。比如像以下的一种定义:

public void xx() {
    class N1 {
    }
}

这里的N1的字节码中就会出现EnclosingMethod属性,并且相应的值即为xx。同时,在class对象上调用getEnclosingMethod也会返回相应的method对象。
同样的处理,出现在getEnclosingClass和getEnclosingConstructor中,都是相同的定义手法。

localVariableTable和localVariableTypeTable属性
同样是针对本地变量(并不完全是方法参数信息)作说明,前者是对名字之类的作说明,后者是对泛型的签名信息作说明,而不是对类型作说明。类型信息在方法信息的描述上即有。

ConstantValue属性
在属性表中的ConstantValue指的是类的静态字段的常量信息。如public static final 这些信息的常量引用,而不是通常所说的初始化值。这个常量由JVM在静态初始化类的时候使用,即仅使用一次。

相关文章:



相关文章

发表评论

Comment form

(*) 表示必填项

还没有评论。

跳到底部
返回顶部