Java 栈内存储器(Stack)
发布时间: 2013-10-31 12:03:52 作者: rapoo
Java 栈内存(Stack)
?
??????? 这里值得注意的是,堆的物理地址向高地址扩展,而栈的物理地址是向低地址扩展,压栈的操作使得栈顶的地址减小,弹出的操作使得栈顶的地址增大。
??????? 每一条JVM 线程都有自己私有的JVM 栈(Java Virtual Machine Stack),这个栈与线程同时创建,用于存储帧(Frames)。JVM 栈的作用与传统语言(例如C语言)中的栈非常类似,就是用于存储局部变量与一些过程结果的地方。另外,它在方法调用和返回中也扮演了很重要的角色。因为除了帧的出栈和入栈之外,JVM 栈不会再受其他因素的影响,所以帧可以在堆中分配,JVM 栈所使用的内存不需要保证是连续的。
??????? JVM 规范允许JVM 栈被实现成固定大小的或者是根据计算动态扩展和收缩的。如果采用固定大小的JVM 栈设计,那每一条线程的JVM 栈容量应当在线程创建的时候独立地选定。JVM 实现应当提供给程序员或者最终用户调节虚拟机栈初始容量的手段,对于可以动态扩展和收缩JVM 栈来说,则应当提供调节其最大、最小容量的手段。
??????? 虚拟机只会直接对Java 栈执行两种操作:以帧为单位的压栈与出栈。某个线程正在执行的方法被称为该线程的当前方法,当前方法使用的帧称为当前帧,当前方法所属的类称为当前类,当前类的常量池称为当前常量池。在线程执行一个方法时,它会跟踪当前类和当前常量池。
??????? 每当线程调用一个Java 方法时,虚拟机都会在该线程的Java 栈中压入一个新的帧,而这个帧自然就成为了当前帧。在执行这个方法时,他使用这个帧来存储参数,局部变量,中间运算结果等数据。
??????? Java方法可以以两种方式结束,一种为通过return 返回,称为正常返回;另一种为通过异常的抛出而中止。无论何种返回方式,虚拟机都会将当前帧弹出Java 栈然后释放掉,这样上一个方法的帧就成为了当前帧。
??????? Java 栈上的所有数据都是此线程私有的,任何线程都无法访问到另一个线程的栈内数据,所以我们不必去担心多线程下的帧数据同步问题。当一个线程调用一个方法时,方法的局部变量保存在调用线程Java 栈的帧中。只有调用方法的线程才能访问到那些局部变量信息。
???????Java代码
- public?static?int?method1(int?a,short?b,long?c,byte?d,Object?e){??
- ????return?0;??
- }??
- ??
- private?void?method2(long?f,double?g,Object?h){??
- ??
- }??
??????? 在局部变量区中各个参数的存储形式:

?
??????? 在图中method2 方法中,局部变量数组第一个参数是reference(引用)类型,尽管我们在方法中没有声明这个参数,但是对于任何一个实例方法都是隐含加入的,他用来表示调用该方法的对象本身,也就是我们常用到的this 关键字。与此相反,method1却没有隐含这个变量,因为method1是一个static 芳芳,也就是类方法,类方法只与类本身有关,而与具体的对象无关,不能直接通过类方法访问类实例的变量,因为在方法调用的时候没有关联到一个具体实例。
??????? 前面已经提到类型为byte、short 和char 的值在存入数组前会被转换成int 值,在操作数栈中也是一样。前面文章提到过虚拟机并不直接支持boolean 类型,因此Java 编译器总是会用int 类型来表示boolean。其他类型是JVM 的基本类型,所以可以比较好的支持。他们在帧中是被当作int 来进行处理的,一旦他们被存回堆或方法区时,会被转换回原来的类型。
??????? 同样的,在Java 中,所有对象都按引用传递,并且都存储在堆中,永远都不会在局部变量区或操作数栈中存在,只会有他们的引用。
??????? 除了Java 方法的参数对于真正的局部变量可以任意觉得放置的顺序,例如在两个for 循环中都会用到int i的情况,当前一个for 循环执行完毕之后,局部变量i 已经超出了有效作用域,所以后面可以继续用i 来表示其他变量。
Java代码
- for(int?i=0;i<10;i++){??
- ????//TODO?Somthing??
- }??
- for(int?i=0;i<10;i++){??
- ????//TODO?Somthing??
- }??
?
???????Class代码
- 0:???iconst_99??????//int?a=99;??
- 1:???istore_0??
- 3:???iconst_100?????//int?b=100;??
- 4:???istore_1??
- 6:???iload_0??
- 7:???iload_1??
- 8:???iadd???????//a+b=199??
- 9:???istore_2??
?
???????Java代码- public?class?StackTest?{??
- ??
- ????public?void?test()?{??
- ????????int?i1?=?5;??
- ????????int?i2?=?5;??
- ????????int?i3?=?6;??
- ????????int?i4?=?128;??
- ????????int?i5?=?32768;??
- ????}??
- }??
Shell代码
- javap?StackTest?-verbose?-c??
C代码
- Compiled?from?"StackTest.java"??
- public?class?StackTest?extends?java.lang.Object??
- ??SourceFile:?"StackTest.java"??
- ??minor?version:?0??
- ??major?version:?50??
- ??Constant?pool:??
- const?#1?=?class????????#2;?????//??StackTest??
- const?#2?=?Asciz????????StackTest;??
- const?#3?=?class????????#4;?????//??java/lang/Object??
- const?#4?=?Asciz????????java/lang/Object;??
- const?#5?=?Asciz????????<init>;??
- const?#6?=?Asciz????????()V;??
- const?#7?=?Asciz????????Code;??
- const?#8?=?Method???????#3.#9;??//??java/lang/Object."<init>":()V??
- const?#9?=?NameAndType??#5:#6;//??"<init>":()V??
- const?#10?=?Asciz???????LineNumberTable;??
- const?#11?=?Asciz???????LocalVariableTable;??
- const?#12?=?Asciz???????this;??
- const?#13?=?Asciz???????LStackTest;;??
- const?#14?=?Asciz???????test;??
- const?#15?=?int?32768;??
- const?#16?=?Asciz???????i1;??
- const?#17?=?Asciz???????I;??
- const?#18?=?Asciz???????i2;??
- const?#19?=?Asciz???????i3;??
- const?#20?=?Asciz???????i4;??
- const?#21?=?Asciz???????i5;??
- const?#22?=?Asciz???????SourceFile;??
- const?#23?=?Asciz???????StackTest.java;??
- ??
- {??
- public?StackTest();??
- ??Code:??
- ???Stack=1,?Locals=1,?Args_size=1??
- ???0:???aload_0??
- ???1:???invokespecial???#8;?//Method?java/lang/Object."<init>":()V??
- ???4:???return??
- ??LineNumberTable:??
- ???line?1:?0??
- ??
- ??LocalVariableTable:??
- ???Start??Length??Slot??Name???Signature??
- ???0??????5??????0????this???????LStackTest;??
- ??
- ??
- public?void?test();??
- ??Code:??
- ???Stack=1,?Locals=6,?Args_size=1??
- ???0:???iconst_5??????//将一个int?类型数据压入到操作数栈中??
- ???1:???istore_1??????//将i1?保存到局部变量表中??
- ??
- ???2:???iconst_5??????//将int?类型数据压入到操作数栈中??
- ???3:???istore_2??????//将i2?保存到局部变量表中??
- ??
- ???4:???bipush??6?????//将一个byte?类型数据入栈??
- ???6:???istore_3??????//将i3?保存到局部变量表中??
- ??
- ???7:???sipush??128???//将一个short?类型数据入栈??
- ???10:??istore??4?????//将i4?保存到局部变量表中??
- ??
- ???12:??ldc?????#15;??//从运行时常量池中提取数据推入操作数栈??
- ???14:??istore??5?????//将i5?保存到局部变量表中??
- ??
- ???16:??return????????//从当前方法返回void??
- ??LineNumberTable:??
- ???line?4:?0??
- ???line?5:?2??
- ???line?6:?4??
- ???line?7:?7??
- ???line?8:?12??
- ???line?9:?16??
- ??
- ??LocalVariableTable:??
- ???Start??Length??Slot??Name???Signature??
- ???0??????17??????0????this???????LStackTest;??
- ???2??????15??????1????i1???????I??
- ???4??????13??????2????i2???????I??
- ???7??????10??????3????i3???????I??
- ???12??????5??????4????i4???????I??
- ???16??????1??????5????i5???????I??
- }??
C代码
- lcd?#15??
- lcd?为操作码,#15为操作数??
- sipush??128??
- sipush?为操作码,128为操作数??
?
Java代码
- public?class?StackTest?{??
- ??
- ????public?void?test()?{??
- ????????String?str="str";??
- ????????String?str2="str";??
- ????}??
- }??
C代码
- Compiled?from?"StackTest.java"??
- public?class?StackTest?extends?java.lang.Object??
- ??SourceFile:?"StackTest.java"??
- ??minor?version:?0??
- ??major?version:?50??
- ??Constant?pool:??
- const?#1?=?class????????#2;?????//??StackTest??
- const?#2?=?Asciz????????StackTest;??
- const?#3?=?class????????#4;?????//??java/lang/Object??
- const?#4?=?Asciz????????java/lang/Object;??
- const?#5?=?Asciz????????<init>;??
- const?#6?=?Asciz????????()V;??
- const?#7?=?Asciz????????Code;??
- const?#8?=?Method???????#3.#9;??//??java/lang/Object."<init>":()V??
- const?#9?=?NameAndType??#5:#6;//??"<init>":()V??
- const?#10?=?Asciz???????LineNumberTable;??
- const?#11?=?Asciz???????LocalVariableTable;??
- const?#12?=?Asciz???????this;??
- const?#13?=?Asciz???????LStackTest;;??
- const?#14?=?Asciz???????test;??
- const?#15?=?String??????#16;????//??hello?world!??
- const?#16?=?Asciz???????hello?world!;??
- const?#17?=?Asciz???????str;??
- const?#18?=?Asciz???????Ljava/lang/String;;??
- const?#19?=?Asciz???????str2;??
- const?#20?=?Asciz???????SourceFile;??
- const?#21?=?Asciz???????StackTest.java;??
- ??
- {??
- public?StackTest();??
- ??Code:??
- ???Stack=1,?Locals=1,?Args_size=1??
- ???0:???aload_0??
- ???1:???invokespecial???#8;?//Method?java/lang/Object."<init>":()V??
- ???4:???return??
- ??LineNumberTable:??
- ???line?1:?0??
- ??
- ??LocalVariableTable:??
- ???Start??Length??Slot??Name???Signature??
- ???0??????5??????0????this???????LStackTest;??
- ??
- ??
- public?void?test();??
- ??Code:??
- ???Stack=1,?Locals=3,?Args_size=1??
- ???0:???ldc?????#15;?//String?hello?world!??
- ???2:???astore_1??
- ???3:???ldc?????#15;?//String?hello?world!??
- ???5:???astore_2??
- ???6:???return??
- ??LineNumberTable:??
- ???line?4:?0??
- ???line?5:?3??
- ???line?6:?6??
- ??
- ??LocalVariableTable:??
- ???Start??Length??Slot??Name???Signature??
- ???0??????7??????0????this???????LStackTest;??
- ???3??????4??????1????str???????Ljava/lang/String;??
- ???6??????1??????2????str2???????Ljava/lang/String;??
- ??
- }??
?
??????? 名词解释:
??????? class:代表类
??????? Method:代表方法
??????? Asciz:声明由JVM 调用,其他无法调用。
??????? Ljava/lang/String; JNI字段描述符,代表了String 类型对象。
??????? 更多解释可以参照JVM 规范。
???????
????????21-25行代码:声明了str 与str2 两个String 类型对象,并且只声明了一个字符串值"hello world!"。
????????47行代码:str 变量从常量池中提取#15 索引的值,这里就需要注意了,#15并不像前面那段代码那样直接存储的是值内容,而是指向#16的一个引用,所以返回给ldc 的就是一个引用类型数据。
????????48行代码:将str 的引用值保存到局部变量表中,astore_<n> 的作用就是将一个reference 类型数据保存到局部变量表中。
????????49行代码:此时的str2 变量直接从#15 索引这里获取了同str 变量一样的对象引用值,所以可以断定在同一个类或方法中,具有相同内容的String 类型对象持有的是相同的引用对象地址。
????????50行代码:将str2 的引用值保存到局部变量表中。
??????? 好了通过以上一些知识相信大家已经对JVM Stack 有了比较深的认识,从短短的一篇文章很难讲的全面,只能抓住一些重点来突出,更进一步的思考还需要在不同的实践中得到印证与结果。
?