Java内存
1. 程序计数器
a) 比较小,Java内存中唯一不会抛出OutOfMemoryError的区域;
b) 线程私有;
c) 用于指示当前线程所执行的字节码地址(行号);
2. 虚拟机栈
a) 描述了Java方法调用的内存模型:方法调用时会生成一个栈帧,包括了局部变量表,动态链接,操作数栈,方法调用出口;
b) 线程私有;
c) 在代码编译期就可以确定所占空间;
d) 通常说的“栈内存”就是特指虚拟机栈的局部变量表:基本类型变量,对象引用,returnAddress;
e) 如果线程请求的栈深度大于虚拟机所允许的深度,会抛出StackOverflowError;如果虚拟机栈可以扩展,且扩展时无法申请到足够的内存,抛出OutOfMemoryError异常;
f) HotSpot通过-Xss设置栈容量;
3. 本地方法栈
a) 与虚拟机栈类似,虚拟机栈为虚拟机执行Java方法服务,本地方法栈为虚拟机使用到的Native方法服务,有的虚拟机将两者实现合二为一(Sun HotSpot);
b) 也会抛出StackOverflowError和OutOfMemoryError异常;
4. Java堆
a) 所有线程共享,Java内存最大的一块,虚拟机启动时创建;
b) 所有对象实例和数组内存的分配(随着技术发展,这也不绝对);
c) 是垃圾收集管理器的主要工作区域,也称为GC堆;
d) 从内存回收角度,现在基本虚拟机都采用分代收集算法,所以Java堆可以分为Old Generation和New Generation,再细致点包括Eden、From Survivor、To Survivor等;
e) 从内存分配的角度,还可以划分多个线程私有的分配缓冲区(TLAB);
f) 可以在物理上处于不连续的空间,但逻辑上是连续的;
g) 基本按照可扩展来实现,通过-Xms(堆最小值)和-Xmx(堆最大值)控制;
h) 当内存无法再扩展时,抛出OutOfMemeoryError异常:java.lang.OutOfMemoryError: Java heap space
5. 方法区
a) 线程共享的区域,用于存放被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,也叫做Non-heap;
b) HotSpot用永久代来实现方法区,不过这种方法将逐步被取代,永久代有-XX:MaxPermSize上限;
c) 不需要连续的内存空间,也可以选择固定大小或可扩展,还可以选择不实现垃圾回收;
d) 会出现OutOfMemoryError异常;
e) HotSpot通过-XX:PermSize和-XX:MaxPermSize设置永久代大小;
6. 运行时常量池
a) 方法区的一部分;
b) 编译期生成各种字面量和符号引用,以及运行期间生成的常量(string的intern方法);
c) 具备动态性,并不一定要求常量一定只有编译期才能产生;
d) 会出现OutOfMemoryError异常;
7. 直接内存
a) 可通过-XX:MaxDirectMemorySize指定,如果不指定则与-Xmx一样大小;
HotSpot对象创建
当New一个对象时:
1. 首先检查指令的参数是否能在常量池定位到符号引用,并检查该符号引用对应的类是否被加载,否则执行类加载过程;
2. 为对象分配内存空间:由Java堆是否规整决定选择指针碰撞或空闲列表,堆是否规整由垃圾收集器是否带有压缩功能决定;
3. 为了保证内存分配时的线程安全,有两种手段:一种是进行同步处理,即CAS配上失败重试;另一种是TLAB;
4. 分配的内存空间初始化;
5. 对对象进行必要设置,将信息放在对象头;
对象在内存中存储的布局包括:对象头,实例数据,和对齐填充。
对象头包括运行时数据(也叫Mark Word)和类型指针;
实例数据是对象真正存储的有效信息,这部分的存储顺序受虚拟机分配策略参数和字段定义顺序影响;
HotSpot要求对象其实地址必须是8字节的整数倍,因此需要对齐填充来补全。
对对象的访问定位,通过栈上的reference来操作堆上的具体对象,可以使用句柄或直接指针。
如果使用句柄,则需要划分句柄池,句柄中包含了对象实例数据指针和对象类型数据的指针,reference中存放的是稳定的句柄地址,不管对象是否被移动都不需修改;
直接指针访问,速度快,节省了一次指针定位开销,在HotSpot应用了该种手段;
是否可以这样理解:
1. 虚拟机启动时根据参数分配各部分内存,程序计数器,虚拟机栈和本地方法栈(-Xss),Java堆(-Xms,-Xmx),方法区(包括运行时常量池,-XX:PermSize,-XX:MaxPermSize),直接内存(-XX:MaxDirectMemorySize)等;
2. 有一个类,在编译时可以确定该类的局部变量(基本数据类型,对象引用等),并在虚拟机栈分配所需内存空间;
3. 线程启动时,程序计数器有一个程序字节码指令的地址;
4. 当新建一个类实例时,会在Java堆中分配内存,而对象引用是放在虚拟机栈的局部变量表中;
5. 虚拟机会加载类的类信息,常量,静态变量等到方法区,其中运行时常量池加载类的字面量和符号引用;
关于直接引用和符号引用:http://blog.youkuaiyun.com/kkdelta/article/details/17752097