Java内存区域

JAVA内存区域划分

程序计数器

程序计数器是一块较小的内存空间,字节码解释器工作时,通过改变该计数器的值来选择下一条需要执行的字节码指令,如果正在执行的是JAVA方法,计数器记录的是正在执行的虚拟机字节码指令的地址,如果执行的是native方法,则计数器值为空。程序计数器是线程私有内存区域。

虚拟机栈

虚拟机栈也是线程私有的,每个方法被执行时,虚拟机会创建一个栈帧用于存储局部变量表、操作数栈、动态连接、方法出口等信息。

  • 局部变量表

    局部变量表中存放着各种基本类型、对象引用、returnAddress类型。

本地方法栈

区别于 Java 虚拟机栈的是,Java 虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。也会有 *Error 和 OutOfMemoryError 异常。在HotSopt虚拟机中将本地方法栈和Java栈合二为一。

JAVA堆

Java虚拟机管理的最大的一块内存区域,几乎所有的JAVA对象的实例都存放在堆中,堆是垃圾收集器管理的区域。Java堆可以是物理上不连续的区域。堆是所有线程共享的内存区域。

方法区

方法区也是所有线程共享的内存区域,用于存放已经被虚拟机加载的类型信息,常量,静态变量,即时编译器编译后的代码缓存。

  • 运行时常量池

    运行时常量池保存着Class文件中常量池表的各种字面量与符号引用,和运行时间所产生的新变量。

直接内存

使用NIO的时候,使用Native函数库直接分配的堆外内存。

Java堆上对象的创建、布局和访问的过程

对象的创建

当碰上字节码指令new时

1. 检查能否在常量池中找到这个类的符号引用,并检查该类是否被加载,解析,初始化过。

2. 如果没有,进入类加载阶段

3. 类加载检查通过后,为新生对象分配内存

  • 指针碰撞分配方式:将堆内存分成一块已经分配过的和一块未被分配过的,中间使用指针来表明分界线,为新生对象分配内存后,将指针向空闲空间挪动。
  • 空闲列表分配方式:堆内存的分配方式并非连续,虚拟机维护一个列表,表面哪些内存空间是可用的,大小是多少。
  • 维护线程安全:分配内存空间不是线程安全的,假设要给对象A分配一块内存空间,还未移动指针,对象B的分配使用了原来的指针。虚拟机一种方法是使用了CAS加上失败重试的方式保证线程安全,另一种方法是将Java堆给各个线程预先分配一块区域,称为本地线程分配缓冲,分配对象首先在本地缓冲区中分配。

4. 将分配到的空间不包括对象头都初始化为0

5. 对对象头进行必要的设置

6. 执行构造方法

对象的内存布局

对象的内存布局可以划分为三部分:对象头、实例数据和对齐填充。

  • 对象头:包含两类信息,一类是对象运行时的数据,例如哈希码,分代年龄,锁状态等;另一类是指向方法区的类型指针,通过该指针来确定该对象是哪个类的实例。
  • 实例数据:用来存放各种类型的字段内容,包括从父类继承的。
  • 对齐填充:HotSpot虚拟机的自动内存管理系统要求对象的起始地址必须是8字节的整数倍,所以要将不满8字节整数倍的对象补齐。

对象的访问

对象的访问方式主要有通过句柄和直接指针访问两种。

  • 通过句柄访问对象:Java栈中的引用类型指向Java堆中的句柄池中的相应句柄地址,句柄中包括到对象实例数据的指针和到对象类型数据的指针,前者指向Java堆中的实例池中,后者指向方法区。
  • 通过直接指针访问对象:Java栈中的引用类型指向Java堆中的对象地址,而对象头中保存着到对象类型数据的指针。

Java内存区域

上一篇:编程-差分数组


下一篇:logback-spring.xml