JAVA程序对它所管理的内存区域进行划分,分为堆、方法区、虚拟机栈、本地方法栈、程序计数器。
image.png1. 程序计数器
- 一块较小的内存空间,线程所私有,看作当前线程所执行的字节码的行号指示器。
- 如果执行的是JAVA方法,计数器记录的是正在执行的虚拟机字节码指令地址;如果正在执行的是本地方法,这个计数器值为空。
- 唯一没有规定任何OutOfMemoryError情况的区域。
2. JAVA虚拟机栈
- 线程所私有,生命周期和当前线程相同。虚拟机栈描述的是JAVA方法执行的线程内存模型:每个JAVA方法执行时都会同步创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态连接、方法出口等信息。
- 局部变量表存放了编译期可知的基本数据类型、对象引用类型、和returnAddress类型。
- 这些护具类型在局部变量表中的存储空间以局部变量槽(Slot)来表示,其中64位长度的long和double类型会占用两个变量槽,其余类型只占用一个。
- 当进入方法时,需要的栈帧分配的局部变量空间是确定的,指变量槽的数量,具体的内存空间需要根据单个变量槽所占用比特决定,由具体虚拟机实现确定。
- 该区域规定了两类异常,分别是StackOverflowError和OutOfMemoryError异常。如果线程请求的栈深度大于虚拟机所允许的深度,抛出StackOverflowError,如果JAVA虚拟机栈容量可以的动态扩展,当栈扩展时,无法申请到足够的内存会抛出OutOfMemoryError。
3. 本地方法栈
- !. 为虚拟机使用到的本地方法服务。
4. JAVA堆
- 是所有线程共享的且是虚拟机管理的最大的一块内存区域,用于存放对象实例。
5. 方法区
- 是线程共享的、用于存储已加载的类型信息、常量、静态 变量、即时编译器编译后的代码缓存等数据。
6. 运行时常量池
- 是方法区的一部分,用于存放类的常量池表,存放编译期生成的各种字面量与符号引用。
7. 对象的创建
image.png7.1 分配内存方式
分配对象时,在类加载通过后就可以确定内存的大小,根据JAVA堆的内存是否规整,内存分配分两类:
-
指针碰撞:JAVA堆内存规整,所有被使用过的内存放一边,未被使用过的内存放另一边,中间放着一个指针作为分界点的指示器,通过往未使用过的内存一边挪动一段与对象大小相等的距离,来完成对对象内存的分配。
-
空闲列表:JAVA堆内存不规整,需要维护一个空闲列表,来标注可用的内存区域,在分配内存时,在列表上找到一块足够大的空间划分给对象使用,并更新列表上的记录。
7.2 安全分配内存
为了在多线程情况下,保证分配对象的线程安全性,有两种方案:
- 采用CAS配上失败重试的方式保证更新操作的原子性。
- 把内存分配的动作按照线程划分在不同的空间中进行。
网友评论