JVM 堆和栈

JVM 堆是一个运行时数据区域,类的对象从堆中分配空间,这些对象通过 new 指令建立,通过垃圾回收销毁。

(注:静态变量存放在方法区中,JDK8 以后,将方法区迁移到了堆中)

堆的优势是可以动态的分配内存空间,编译器并不知道需要分配多少内存给堆,也不知道堆汇总的数据存活时间,因为堆内存是在运行时动态分配的,比较自由;

对于这个优势,JVM 也要响应的损失一部分性能与时间分配内存。

栈中主要存放一些基本数据类型的变量(boolean,byte,short,int,long,floa,doublie,char)

栈的优势是存取速度比堆快,栈中数据可共享。

与堆不同,在栈创建的时候,编译器必须确切知道栈中的所有数据的确切大小和生命周期,因为要生成相应的代码,缺乏一定的灵活性。

Java 中的基础类型

类型 值域 默认值 虚拟机内部符号
boolean {false, true} false Z
byte [-128, 127] 0 B
short [-32768, 32767] 0 S
char [0, 65535] ‘\u0000’ C
int [-2^31, 2^31-1] 0 I
long [-2^63, 2^63-1] 0L J
float ~[-3.4E38, 3.4E38] +0.0F F
double ~[1.8E308, 1.8E308] +0.0D D

类型存储

JVM 学习笔记一(Java 代码的执行) 中说道,调用一个方法时,JVM 会在 Java 方法栈中生成一个栈帧存放方法运行时数据,而且栈帧的大小是计算好的(上文中也有提及),在栈帧中存放着这个方法的 局部变量字节码操作数栈实例方法的 this 指针形参

(栈帧 = 局部变量 + 字节码操作栈 + 实例方法 this 指针 + 形参)

在 JVM 规范中,局部变量相当于一个数组,并且可以用正整数来索引,除了 long 和 double 需要两个数组单元存储之外,其他基本类型或引用类型均占用一个数组单元,在栈空间上和 int 所占空间一样。(在 64 位的机器中占用 8 字节,32 位机器中占用 4字节)

以上情况仅仅出现于栈上,也就是说只会出现在方法区中;在堆中,这些类型该是几个字节还是几个字节,比如 boolean,还是只占用 1 字节。

类型加载

JVM 算数运算基本依赖于操作数栈,需要将堆中的 boolean,byte,char,short 加载到 操作数栈上,映射成 int 类型进行运算。如果是 byte 和 short 映射成 int,低位由 byte 和 short 值补充,高位若为正数补充 0,负数补充 1。

网络摘录:boolean 字段和 boolean 数组则比较特殊。在 HotSpot 中,boolean 字段占用一字节,而 boolean 数组则直接用 byte 数组来实现。为了保证堆中的 boolean 值是合法的,HotSpot 在存储时显式地进行掩码操作,也就是说,只取最后一位的值存入 boolean 字段或数组中。

(注:这里说的映射成 int 类型只是说加载到栈帧的时候,存储在堆里面的时候,以上 4 种类型仍然是 1字节,1字节, 1字节,2字节)