Java LineNumberTable:条目说明

时间:2016-12-12 15:42:29

标签: java class bytecode

如果我反汇编我的类文件,我会得到格式为

的LineNumberTables
  LineNumberTable:
    line 204: 0
    line 205: 9
    line 208: 57
    line 209: 63
    line 210: 72
    line 211: 75
    line 212: 78
    line 213: 87
    line 216: 90
    line 218: 118
    line 221: 126
    line 222: 131
    line 223: 138
    line 224: 143
    line 227: 150
    line 230: 157
    line 231: 160
    line 232: 170
    line 235: 194
    line 237: 228
    line 240: 249
    line 241: 259
    line 243: 266
    line 245: 269
    line 246: 292
    line 248: 295
    line 249: 301
    line 250: 308
    line 251: 315
    line 252: 322
    line 253: 329

我知道这些表包含调试信息,第一个条目是类文件中的某种位置,而第二个条目是源代码中的位置。我想知道:

  1. 源代码行号是相对还是绝对?如果我绝对解释它们,有些人会指向多行评论的中间,这似乎很奇怪。

  2. 相同源代码的两个不同编译仅在一个字节上不同:条目“第216行:90”被“第215:90行”替换。我试图找出原因可能是什么原因。有什么想法吗?

1 个答案:

答案 0 :(得分:3)

将常识应用于您正在阅读的内容。正确阅读规范时,即LineNumberTable属性数组中存储的第一个数字是字节代码偏移量,第二个数字是行号,这并不意味着您正在使用的反汇编程序也会打印它们按此顺序。

交换订单有两个指标

  1. 第一个数字前面印有“Line”字样,表示第一个数字是行号
  2. 第一个数字的范围是204到253,这对于类声明中某个方法的源代码行是合理的,而第二个数字的范围是0329,对于方法中的字节码偏移是合理的,从零开始。

    相反,对于方法,不太可能看到行号从零开始,因为源代码通常以packageimport声明开头。如果方法代码的前203个字节没有相关的源代码行(虽然这不是不可能的),这也是不寻常的。

  3. 两个指标都非常强劲。然后,观察到的变化是合理的。显然,生成的代码没有改变。但由于没有关于行号和生成的代码如何关联的标准,因此可能存在细微差别,具体取决于编译器版本,例如:当一个表达式跨越多行,但只生成一条指令或者当编译器试图避免过多的行号表时。

    E.g。代码

    foo(
    );
    

    只生成一条指令(如果foo()static)并且未定义,则与该指令关联的两条线中的哪一条。当它是一个实例方法时,它将由两个指令组成,但将它们表示为不同的行号将是有争议的,因为在调试期间插入它们将没有多大帮助。但这是编译器的决定。还有

    foo(
        null,
        1,
        true
    );
    

    将每个常量参数推送到堆栈需要指令序列中的一个字节,而将不同的行号与每个指令相关联将需要每个指令另外四个字节。由于推送这些常量不太可能失败,因此,跟踪它们没有多大意义,编译器可能决定将整个序列与调用指令的唯一行号相关联。由于此决定取决于实际编译器,甚至可能取决于其当前配置,因此重新编译可能会更改关联。

    另一个区别是编译器如何处理合成方法,例如桥接方法和内部类访问器。到目前为止,我已经看到它们只与零相关联,周围的类声明的开头,以及它们委派的实际目标成员的开头。