[讨论] LocalVariableTable有点迷糊

xgj1988 2011-04-30
先给个汇编之后的一段代码

private Test  fn(String s){
		String y="123";
		System.out.println("123");
		return null;
	}



private Test fn(java.lang.String);
  Code:
   Stack=2, Locals=3, Args_size=2
   0:	ldc	#35; //String 123
   2:	astore_2
   3:	getstatic	#37; //Field java/lang/System.out:Ljava/io/PrintStream;
   6:	ldc	#35; //String 123
   8:	invokevirtual	#43; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   11:	aconst_null
   12:	areturn
  LineNumberTable: 
   line 11: 0
   line 12: 3
   line 13: 11
  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      13      0    this       LTest;
   0      13      1    s       Ljava/lang/String;
   3      10      2    y       Ljava/lang/String;

}


  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   0      13      0    this       LTest;
   0      13      1    s       Ljava/lang/String;
   3      10      2    y       Ljava/lang/String;
这一块不是很懂。
这一块我想应该对应的是JVM 规范的  如下一块把(红色部分)
LocalVariableTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 local_variable_table_length;
    {  u2 start_pc;
        u2 length;
        u2 name_index;
        u2 descriptor_index;
        u2 index;
    } local_variable_table[local_variable_table_length];
    }

但是感觉对应不上:
1:名字都对不上
2:start_pc 可以和start 对应,这个应该没什么问题。
3:Length和 length对应也没问题。
4:问题就是name_index 和Slot吗?name和sigature和睡对应呢?
5:slot是什么意思呢。为什么会按照先后顺序成递增形式呢?
xgj1988 2011-04-30
额  sigature 应该是和 descriptor_index  ,name_index 和 name  他们都在utf8表里面找,index原来对应的是本地变量数组索引,。额。。当时突然访问不聊官网,现在看了一下了解了。

那么slot就是引用的意思了对吧。。
xgj1988 2011-04-30
对这句话不是太理解。。

希望翻译一下:

start_pc, length
The given local variable must have a value at indices into the code array in the interval [start_pc, start_pc+length], that is, between start_pc and start_pc+length inclusive. The value of start_pc must be a valid index into the code array of this Code attribute and must be the index of the opcode of an instruction. Either the value of start_pc+length must be a valid index into the code array of this Code attribute and be the index of the opcode of an instruction, or it must be the first index beyond the end of that code array.
IcyFenix 2011-04-30
以下供LZ参考:


另外,建议要学习Class文件结构的话,写一个简单的类,用hex编辑器打开对着看(用javap把常量池输出记录一下)是很好的办法,例如下面这样:

RednaxelaFX 2011-04-30
咦?那啥,LocalVariableTable在javac的实现里默认是不生成到Class文件里的吧。默认生成的调试符号信息只有LineNumberTable而已。

楼主的问题:slot是什么?
一个slot就是JVM规范对局部变量区里存储一个局部变量的存储单元的叫法。每个slot可以存储一个宽度在32位或以下的原始类型值,或者一个引用,或一个returnAddress;相邻的两个slot可存储一个double或long型的值。
xgj1988 2011-04-30
额。。了解了。slot.但是 对于
start_pc, length
The given local variable must have a value at indices into the code array in the interval [start_pc, start_pc+length], that is, between start_pc and start_pc+length inclusive. The value of start_pc must be a valid index into the code array of this Code attribute and must be the index of the opcode of an instruction. Either the value of start_pc+length must be a valid index into the code array of this Code attribute and be the index of the opcode of an instruction, or it must be the first index beyond the end of that code array.

这句话。Lcy的解释是他的声明周期,这样的话,
  LocalVariableTable:   
   Start  Length  Slot  Name   Signature  
   0      13      0    this       LTest;  
   0      13      1    s       Ljava/lang/String;  
   3      10      2    y       Ljava/lang/String; 

这个的声明周期从start+length 如何体现呢?3个值的结果都是13(start+length),我只知道这样表示他们的范围都是一样的,但是可以看到y变量的start为3,length为10,而s和this表示的是start=0,length=10。其实这样也可以理解。因为this,和s 是方法一开始就被赋予的局部变量,所以他们的偏移量肯定比y大,而且肯定是0开始,但是为什么length 是13呢 以及 y的start是3,以及length是10,如何计算的。为什么不是start=1 length=12呢?,还有就是为什么他们这里的总和是13,而不是15,16呢?

我把上面代码改成如下形式
private Test fn(String s) {
		String y = "123";
		System.out.println("123");
		int x=10;
		{
			int z=5;
		}
		return null;
	}

这个时候会得到
 
LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      19      0    this       LTest;
   0      19      1    s       Ljava/lang/String;
   3      16      2    y       Ljava/lang/String;
   14      5      3    x       I


这样得到的结果有点以为 因为 z变量不在了,对于JVM 这里的Z的范围如何来表示呢?如果说this,s,y,x使用LocalVariableTable表示,那么这个z是放在什么地方?他处于一个什么样的位置呢?

总结一下我迷糊的地方:
1:start的值是根据什么规则计算的,length的值是根据什么规则计算的?
2:第二个方法里面的z的变量的范围是如何表示出来的(JVM如何知道这个z的变量超出了某个范围,因为他不在LocalVarableTable里面),也就是Z去哪里了?
xgj1988 2011-04-30
呵呵随便更正一下lcy的,red也说了。JAVAC默认是带LineNumberTable,只是没有LocalVariableTable,不然不能调试的。
RednaxelaFX 2011-04-30
xgj1988 写道
额。。了解了。slot.但是 对于
start_pc, length
The given local variable must have a value at indices into the code array in the interval [start_pc, start_pc+length], that is, between start_pc and start_pc+length inclusive. The value of start_pc must be a valid index into the code array of this Code attribute and must be the index of the opcode of an instruction. Either the value of start_pc+length must be a valid index into the code array of this Code attribute and be the index of the opcode of an instruction, or it must be the first index beyond the end of that code array.

这句话。Lcy的解释是他的声明周期,这样的话,
  LocalVariableTable:   
   Start  Length  Slot  Name   Signature  
   0      13      0    this       LTest;  
   0      13      1    s       Ljava/lang/String;  
   3      10      2    y       Ljava/lang/String; 

这个的声明周期从start+length 如何体现呢?3个值的结果都是13(start+length),我只知道这样表示他们的范围都是一样的,但是可以看到y变量的start为3,length为10,而s和this表示的是start=0,length=10。其实这样也可以理解。因为this,和s 是方法一开始就被赋予的局部变量,所以他们的偏移量肯定比y大,而且肯定是0开始,但是为什么length 是13呢 以及 y的start是3,以及length是10,如何计算的。为什么不是start=1 length=12呢?,还有就是为什么他们这里的总和是13,而不是15,16呢?

我把上面代码改成如下形式
private Test fn(String s) {
		String y = "123";
		System.out.println("123");
		int x=10;
		{
			int z=5;
		}
		return null;
	}

这个时候会得到
 
LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      19      0    this       LTest;
   0      19      1    s       Ljava/lang/String;
   3      16      2    y       Ljava/lang/String;
   14      5      3    x       I


这样得到的结果有点以为 因为 z变量不在了,对于JVM 这里的Z的范围如何来表示呢?如果说this,s,y,x使用LocalVariableTable表示,那么这个z是放在什么地方?他处于一个什么样的位置呢?

总结一下我迷糊的地方:
1:start的值是根据什么规则计算的,length的值是根据什么规则计算的?
2:第二个方法里面的z的变量的范围是如何表示出来的(JVM如何知道这个z的变量超出了某个范围,因为他不在LocalVarableTable里面),也就是Z去哪里了?

不知道你是怎么看的…
public class Test {
  private Test fn(String s) {
    String y = "123";
    System.out.println("123");
    int x=10;
    {
      int z=5;
    }
    return null;
  }
}

private Test fn(java.lang.String);
  Code:
   Stack=2, Locals=5, Args_size=2
   0:	ldc	#2; //String 123
   2:	astore_2
   3:	getstatic	#3; //Field java/lang/System.out:Ljava/io/PrintStream;
   6:	ldc	#2; //String 123
   8:	invokevirtual	#4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   11:	bipush	10
   13:	istore_3
   14:	iconst_5
   15:	istore	4
   17:	aconst_null
   18:	areturn
  LineNumberTable: 
   line 3: 0
   line 4: 3
   line 5: 11
   line 7: 14
   line 9: 17

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   17      0      4    z       I
   0      19      0    this       LTest;
   0      19      1    s       Ljava/lang/String;
   3      16      2    y       Ljava/lang/String;
   14      5      3    x       I

变量z的信息不是在那里么。

start和length组合起来就是表示作用域。
如果源语言是Java的话,中start会从某个slot被某个变量的首次赋值(Xstore)之后的一条指令开始算,范围到该作用域所包含的最后一条指令为止。

局部变量区里,一个slot被某个变量占用的范围是[start, start+length],留意到两端都是包含的。所以length为0说明某个变量只在某条指令当中才占用了指定的slot,后面马上就不继续占用了。

看这个例子:
public class Test {
  private void foo() {
    {
      int a = 1;
      int b = 2;
    }
    {
      float c = 3f;
    }
    {
      long d = 4;
    }
  }
}

private void foo();
  Code:
   Stack=2, Locals=3, Args_size=1
   0:	iconst_1
   1:	istore_1
   2:	iconst_2
   3:	istore_2
   4:	ldc	#2; //float 3.0f
   6:	fstore_1
   7:	ldc2_w	#3; //long 4l
   10:	lstore_1
   11:	return
  LineNumberTable: 
   line 4: 0
   line 5: 2
   line 8: 4
   line 11: 7
   line 13: 11

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   2      2      1    a       I
   4      0      2    b       I
   7      0      1    c       F
   11      0      1    d       J
   0      12      0    this       LTest;

这个例子里,可以看到变量a是在字节码偏移量1的指令istore_1开始被赋值的,所以它的作用域从之后的一条指令(偏移量为2)开始。而变量b是在字节码偏移量3的位置被首次赋值的,它的作用域就从后一条指令(偏移量为4)开始。但,字节码偏移量为4的指令已经在a、b的作用域之外,所以你可以看到a的作用域就到偏移量4,b也一样(也就是a和b两变量的start_pc+length都为4说明它们是在同一个作用域里声明的)。
另外,这个例子也体现了slot的可复用性。在同一个方法里,作用域没有交集的变量可以复用同一个slot。
xgj1988 2011-04-30
额。首先对我的大意表示抱歉。

然后再对 Red的讲解 表示感谢,说得很详细。呵呵
xgj1988 2011-04-30
额,发现个问题十分奇怪

public class Test {
	public static void main(String[] args) throws Exception {
		{
			int z=0;
		}
	}

}

下面分别是ecj 和 javac 编译之后的代码。然后都用javap -private -vervose -c 这样的参数来打出的他的Class文件内容。

结果不同。(这里我只贴出LocalVariableTable的信息,其他的好像都一样)
ECJ 编译之后 JAVAP的代码

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      3      0    args       [Ljava/lang/String;

javac -g之后 JAVAP的代码


  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   2      0      1    z       I
   0      3      0    args       [Ljava/lang/String;

看到没,ECJ的确实没有 z 这个变量的信息。额。而且javac -g之后的信息还有 异常表信息  而 ecj的没有
Exceptions:   throws java.lang.Exception

问题:
1:为什么ECJ之后的代码看不到z变量呢?
2:为什么z变量在localvariabletable为什么会在第一个,为什么不是this在第一个,因为按如果按照源代码的顺序的话,而且可以看到在方法里面{}里面的局部变量都被放在了LocalVariableTable的前面最前面。何解呢?
Global site tag (gtag.js) - Google Analytics