JVM是基于栈执行的,每个线程会建立一个操作栈,每个栈又包含了若干个栈帧, 每个栈帧包含了局部变量、操作数栈、动态连接、方法的返回地址信息等。其实在我们编译的时候,需要多大的局部变量表、操作数深度等已经确定并写入了Code属性,因此运行时内存消耗的大小在启动时已经已知。
在栈帧中,最小的单位为变量槽(Variable Slot),其中每个Slot占用32个字节。在32bit的JVM中32位的 数据类型占用1个Slot,64bit数据占用2个Slot;在64bit中使用64bit字节填充来模拟32bit(又称补位),因此我们可以得出结论:64bit的JVM比32bit的更消耗内存,但是又出32bit机器的内存上限限制,有时候牺牲一部分还是值得的。Java的基本数据类型中,除了long、double两种数据类型为64bit以外,boolean、byte、char、int、float、reference等都是32bit的数据类型,以及其他变量也只占用一个变量槽,仅仅作为一个引用。
在栈帧中,局部变量表中的Slot是可以复用的,如在一个方法返回给上一个方法时就可以通过公用Slot的方法来节约内存控件,但这在一定程度上会影响垃圾回收,因此JVM不确定这块Slot空间是否还需要复用。
局部变量并没有类实例变量那样的连接过程,前面我们说过,类的加载分为加载、连接、初始化三个阶段,其中连接分为验证、准备、解析三个阶段,而验证是确保类加载的正确性、准备是为类的静态变量分配内存,并初始化为默认值、解析是把类中的符号引用转换为直接引用。而外面的初始化为类的静态变量赋值为正确的值。而局部变量并没有连接的阶段,因此没有赋值为默认值这一阶段,因此必须自己初始化才能使用。所以在局部变量中使用String s;然后又使用s的方法会报错。