学然后知不足!本文参考《深入理解JVM》周至明著。由于写作水平和阅历有限,本中存在不妥之处,还请大家多多留言。
目录
程序计数器(Progeam Counter Register)
java虚拟机运行时数据区。
程序计数器、虚拟机栈、本地方法栈3个区域随线程而生,随线程而灭;
堆、方法区被所有线程所共享。
JVM运行时数据区如下图所示:
程序计数器(Progeam Counter Register)
当前线程所执行的字节码文件的行号指示器。
用途:字节码解释器通过改变这个计数器的值来获取下一条所执行的字节码指令。
不同:执行java方法,记录的值是虚拟机字节码指令地址;执行Native方法,值为空(Undefined)。
Java虚拟机栈
java方法执行的内存模型:每个方法执行同时就创建一个栈帧(Stack Frame)用于存储方法基本信息。方法从开始调用到执行完成,对应一个栈帧在虚拟机栈中入栈到出栈的过程。
出现异常:
-
线程请求的栈深度大于虚拟机所允许的深度。抛出StackOverflowError异常。
-
当虚拟机栈扩展到无法申请到足够的内存(一般虚拟机栈可以动态扩展),就抛出OutOfMemoryError异常。
本地方法栈
区别:
- 本地方法栈:为虚拟机使用到的Native方法服务。
- java虚拟机栈:为虚拟机使用到的java方法(字节码)服务。
堆(Heap)
特点:所有线程所共享,在虚拟机启动时创建;可处于物理上不连续的内存空间。
目的:存储对象实例。
异常:没有内存完成实例分配,并且堆无法再扩展,就抛出OutOfMemoryError异常。
方法区(Method Area)
特点:所有线程所共享。
用途:已被java虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
运行时常量池(Runtime Constant Pool)
方法区一部分。
用途:存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量存放。
异常:内存受方法区内存限制,当常量池无法再扩展,就抛出OutOfMemoryError异常。
HotSpot虚拟机对象
对象创建
-
类加载:在常量池中判断是否初次加载过了,有则加载。
-
JVM为新生对象分配内存,2种方式(根据垃圾收集器)
-
指针碰撞:在使用Serial、ParNew等带Compact过程的收集器,采用其方式分配。
-
空闲列表:使用CMS基于Mark-Sweep算法收集器。
-
初始化
对象的内存布局
内存中对象布局可分为3部分:对象头(Header),实例数据(Instance Data)、对齐填充(Padding)
-
对象头:存储对象自身的运行时数据;类型指针;
-
即类的元数据的指针,可确定对象属于哪个类的实例。
-
对齐填充:占位符作用
对象的访问定位
-
句柄和直接指针两种
句柄:java堆中一块句柄池的内存,引用存储对象的句柄地址,句柄有对象实例数据和类型数据各自地址信息。
特点:在引用中存储稳定句柄地址,对象移动引用不需要修改。
- 直接指针:直接存储对象的地址。
特点:速度快,节省一次指针定位时间。