第一章:自动内存管理机制
1.1 运行时数据区域
1.2 程序计数器
可以看作当前线程执行指令的行号指示器,线程私有的,如果线程执行的是一个java方法,那么这个计数器记录的是正在执行的虚拟机字节码指令的地址,如果执行的是Native方法,这个计数器值则为空。此内存区域是在虚拟机规范中唯一一个没有规定任何OutOfMemoryError情况的区域。
1.3 java虚拟机栈
线程私有的,虚拟机栈描述的是Java方法执行的内存模型,一个方法的执行都包含了栈帧从入栈到出栈的整个过程。
1.4 本地方法栈
本地方法栈和虚拟机栈所描述的功能很相似,只不过虚拟机栈是为JAVA方法也就是字节码服务,而本地方法栈是为本地Native方法服务。与虚拟机栈一样,本地方法栈也会抛出StackOverflowError和OutOfMemoryError。
1.5 java 堆
被所有线程共享的一块内存区域,是垃圾回收器回收的主要区域,常被成为GC堆。由于收集器现在都常用分代收集算法,所以可以划分为:新生代和老年代。Java堆可以处于物理上不连续的内存空间中,只要逻辑上连续即可。
1.6 方法区
用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。
1.7 运行时常量池
常量池用于存放编译期生成的各种字面量和符号引用,这一部分内容将在类加载后进入运行时常量池中存放。
1.8 直接内存
避免了在java堆和Native堆中来回复制数据,显著提高性能。
1.9 对象的创建
虚拟机遇到一条new指令时,首先去检查这个指令的参数是否在常量池中定义到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载,解析和初始化过。如果没有,必须先执行初始化的类加载过程。
假设Java堆中的内存是规整的,一边放使用的所有使用过的内存,另一边为未使用的内存,中间放着一个指针作为分界点的指示器,那分配内存就是仅仅把指针向空闲的一边移动一段和对象大小相等的距离,这种分配方式成为“指针碰撞”。如果Java堆的内存并不规整,使用过的内存和未使用的内存相互交叉,那么就没有办法进行指针碰撞,这时候就需要虚拟机维护一个列表出来,记录那些内存是可用的,在内存分配时从列表中找出一个足够用的内存划分给对象实例,并更新列表上的记录,这成为“空闲列表”,选择哪种分配方式,取决Java堆是否规整,而Java堆是否规整取决于所采用的垃圾回收器是否具有压缩整理功能决定。
为解决分配内存线程同步问题通常采用CAS配上失败重试的方式保证更新操作的原子性,另一种是把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲。
2.0 对象的内存布局
对象在内存中的布局可分为:对象头,实例数据和对齐填充。
2.1 对象的访问定位
Java程序需要通过栈上的reference数据来操作堆上的具体对象,目前主流的访问方式有使用句柄和直接指针两种。
2.2 Java堆内存溢出
Java堆用于存储对象实例,只要不断的创建实例对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在对象数量到达最大堆的容量限制之后就会产生内存溢出异常。