初探 JVM 运行时数据区域

  笔者做为Java小菜鸡,近期在看JVM时发现本身很容易忘记JVM最基础的部分:运行时数据区域大体划分,特此摘记下来以便未来重温。java

  话很少说,先上示意图Java Virtual Machine Memory 程序员

  正如图中所示,JVM数据区大体分为:程序计数器、Java虚拟机栈、本地方法栈(有些虚拟机厂商会选择将其与虚拟机栈结合)、Java堆、方法区(包含运行时常量池)以及直接内存(这部分实际上不属于Java虚拟机,可是在Java堆中有变量DirectByBuffer能够直接指向该区域)算法

  依次记录下本身对这些区域的理解:并发

  • 程序计数器:属于线程私有的数据区,即每条线程都会有本身的程序计数器,这是一片比较小的内存区域,用来指示当前线程下一条执行的指令字节码的地址。有一点须要注意的,当线程执行的不是Java方法,而是Native方法的时候,程序计数器会设为空(Undefined)。
    • 该区域是惟一一片没有规定任何OutOfMemoryError的区域
  • Java虚拟机栈:与程序计数器同样,是属于线程私有的,用来记录Java方法执行的内存模型
    • Java的方法在执行的时候都会为其建立一个栈帧,栈帧内保存的是此方法的局部变量表、操做数栈、动态连接、方法出入口等,每个方法在执行期间都对应着一个栈帧。
    • 局部变量表中存放的是编译期间所知的各类数据类型(基本数据类型+对象引用+returnAddress类型),进入方法时,此方法在帧中分配多大的局部变量表就彻底肯定了,而且在运行期间是不会改变变量表的大小的
  • 本地方法栈(Native Method Stack):与虚拟机栈的做用及其类似,当时就笔者理解,Native栈负责记录的是Native服务或者是其余方法的信息。在有些虚拟机中会将这部分与虚拟机栈合并(如: Sun Hot Spot)
  • Java 堆:分配内存区域最大的一块,几乎全部的对象实例都存放在这片区域。特别注意:这块区域是多条线程共享的区域
    • Java堆也是 GC (Garbage Collection)的主要区域,从GC收集器所使用的 分代收集算法 来看,这一部分又能细分为:新生代与老生代,再进一步细分能够划分为:Eden空间(永生代 占比8)、From Survivor 空间(From存活区 占比1)、To Survivor空间(To存活区 占比1)。可是从内存分配的角度来看,因为做为共享的区域,其也可划分为多个线程私有的分配缓冲区(Thread Local Allocation Buffer),也就是咱们俗称的线程变量副本区域。
    • Java堆不要求使用连续的存储空间,只需逻辑连续便可
  • 方法区(Method Area):与Java堆同样,都属于多条线程共享的内存区域,它主要用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码(IL)等数据。
    • 又称Java 堆的逻辑区,可是与Java堆是由区分的。
    • 能够选择不对方法区进行GC。由于此区域GC的出现频率较低,也主要是针对常量池的回收和已加载类的卸载。
  • 运行时常量池(Runtime Constant Pool):隶属方法区的一部分,在不少地方会单独将此部分拿出来讨论。
    • Class文件中除了类的基本信息(版本、字段、方法、接口等)还有此类的常量池(存放编译期间生成的各类字面量和符号引用),而这个常量池即是存放于运行时常量池中的(在类加载后)
    • 运行期间也能够对常量池进行扩充,例如:String.intern() 将会将此字符串对象的值写入常量池中并返回其在常量池中的引用。
  • 直接内存:笔者的理解,与本地方法区的意图类似,借用非JVM的内存(本机的内存)进行操做。

  重新建一个对象的角度来看,当new关键字出现后,JVM会去检查new后的类是否已经加载到了方法区中(类的常量池等),若已经加载,则根据其所需内存在Java堆中为准备进行的实例化操做划分出一片内存区域。每次分配内存的时候,实际上会根据JVM采用的不一样的收集器决定其内存的分配方式(取决于收集器是否具备压缩整理的功能)大体分为两种分配方式:jvm

  1. 指针碰撞(Bump the pointer) 将内存区域划分为两片区域,一边是已经分配的内存区,一边是未分配的,中间依靠一个指针进行内存的分配(须要分配新的内存则将指针向下移动)。这种方式能确保内存分配的规整。
    • 使用常见的Serial、ParNew收集器(带有Compact过程)会采用此分配方式
  2. 空闲列表(Free list)不要求内存的规整,须要维护一个记录内存分配状况的列表。
    • 使用基于 标记-清理(Mark-Sweep) 收集算法的收集器(如:CMS)会采用此分配方式

同时,有常见的两种方案用于保证能支持并发分配内存:对内存分配采用同步的机制以及采用TLAB。在内存分配后,JVM就会对分配的内存空间进行初始化(设为零值),而后确认该对象的类型、运行状态等信息(设置对象的对象头)。在完成上述的步骤后,从JVM的角度看,一个新对象已经生成了,接下来就开始执行程序员设置的init部分,按照咱们的意愿对此对象进行初始化了。ide

补充说明下:wordpress

  • 对象的内存分配包含三块区域:对象头、实例数据、对齐填充。
  • 对象的两种定位方式:句柄和直接指针
相关文章
相关标签/搜索