原来虚拟机还有一个返回值的类型,returnAddress
在帧中的本地变量中,其变量的内存都是编译时就分配的,在存放在一个数组中.
其中有趣且熟悉的是,Long
或Double
类型的变量会占两个坑,就是说,在LocalVaraiable[]
中,需要用两个Index来存放一个元素,可想到,这就是C中的数组啊,一个元素是32位。而Long
和Double
都是64位的,所以,一切在不言中。
最近看了Python的语法,看到每个方法前面都要写一个self
,而在java中就是this
的意思,每一个方法的调用,都要传递调用者作为第一个参数,因而,调用一个方法,就有两种形式了:
instance.foo(parameters...)
Class.foo(instance,paramters...)
回到这个帧的本地变量,原来LocalVaraiables[0]就默认指定了当前实例中的this
。之后就是参数来了。
Oparand Stack 操作栈
在每一帧中都会存在一个栈,当context 确定后,即该帧在正在被操作时,就简单地把这个帧中的栈称之为操作栈
了。
没看懂一个帧中的栈会在编译时就能确定深度?
额,打出这句疑惑时好像明白了。
开始的疑惑归咎于?
将方法和帧的概念混淆了。
方法可以有嵌套,若是递归,编译时怎么会知道要多少次,这个栈并不是简单的对应的每一个方法,
帧是比方法更小的单位。
大概就先理解到这了。
操作栈的大致的情况就是,有一个context,丢几个进去,返回一个东西。比如add(x,y),先入x,y,来一个操作符add,计算,将x,y丢出来,将结果入栈保存。
发现有点看懵,前面的概念没有弄清,重新梳理下运行时的数据储存区。
Run-Time Data Area 运行时存储区
首先先分两个点,java程序启动后,作为一个进程,启动后有一个公共的数据区,作用域为进程,而另一个小范围的是一个线程作用域的。
the pc Register pc寄存器
因为要支持多线程嘛,所以每个线程是分配有一个存放PC寄存器的。PC寄存器就是用来记录当前程序到指令位置,若是当前运行的java方法,则PC保存java程序正在执行指令的位置,若是执行的是native方法,那么其保持的值是undefined。不知道为啥,最后又来一句,The JVM’ pc register is wide enough to hold returnAddress
or a native pointer on the specific plaform.
JVM Stack (以前叫做Java stack)
这个Java stack会在线程创建时创建,并在在其中保存很多个帧(frame)。 类似C语言中的Stack,会持有本地变量,部分结果,用于方法调用和返回。这个栈的操作单位是frame, 将其入栈出栈。
帧和栈的关系呢?
栈可以固定,或是动态的(即可以配置最小和最大范围)。
Heap
堆是在java虚拟机启动后创建的,即其作用域是跨过所以线程的,这也就是为什么要多个线程访问一个对象时需要加锁的原因了。其保存的包括对象以及数组。
如栈一样,同样可以fixed或dynamic.
Method Area
方法区也全线程共享的。
存放的内容包括编译后的字节码,以及一些text
.
存放类的结构,包括:
- run-time constant pool 运行时常量池
- field 域,变量
- method data
- the code for methods android constructions
看起来就是存放我们的代码指令,一般不会变动太大,所以有的选择作为堆的一部分。或者单独拿出来,因为变动不是很大。
Run-Time Constant Pool
常量池是class
或interface
中constal_pool table中包含的,包括numbers iterails
andfiled refrences
.这个域引用,会在运行时被解析,指向确定的对象实例。
嗯,它还说,就类似往常的编程语言中的符号表symbel table
。(然鹅,我对常见编程语言没概念啊~)
这里,这个还指出了:常量池跟之后要重点学习的,loading,linking, initailizing关系大大的。
Native Method Stack
为了支持Native方法了,调用本地方法就滚到这个栈去了。
2.6 Frame
这个重要,一个帧会在一个方法调用时创建,并在结束时销毁。里面涉及到几个概念。
- Local Variables
- Operand Stack
- Dynamic Linking
- Nomal Method Invocation
- Abrupt Method Invocation
本地变量
在编译时就确定其长度,并保存在字节码中。
作为一个数组来保存这些变量,其值类型很有趣:
数组元素是32位,单个元素可保存boolean,byte,char,short,int,float,reference, or returnAddress.
用两个坑保存Long或Double.
在调用方法时,使用本地变量来传递参数的,第一个元素是调用该方法的对象的引用。之后才是其他参数。
猜想,既然本地变量存在一个
returnAddree
,那个一个方法签名中也包括返回值类型,那么在方法调用完成后,便把结果赋予返回值得中。即完成了结果返回。
Operand Stack 运算元栈
每一个帧中包含栈又叫做operand stack
.
对之前的疑惑更清楚了一点,为什么在编译时,这个栈的深度就确定了。
这个栈在在frame是在调用到该方法时,即context is clear。
用来准备要传递的数据,以及接受结果。
这是真正的运行,会有专门的指令将数据,操作符入栈,出栈。执行最细节的操作。
如,开始开始描述的。。
Dynamic Liking动态链接
根据个人理解,一个Class中定义的仅是一个方法要做的动作。而这个动作的执行者在这个动作定义中是没有确定的,所以动态链接就是要确定这个执行者具体是谁。当然还包括,形参到实参的转变,。
方法正常结束
解释很正常,无异常结束该方法调用。(包括返回其预期想要的结果类型)
若是当前frame是用来,restore the state of invoker, including its local variables and operand stack. PC可能会直接跳过方法。。
(好吧,不理解…) 难道是break ?