Fork me on GitHub

深入理解Java虚拟机之Java内存区域

运行时数据内存

线程私有的内存区域

每条线程都有,各线程之间互不影响,独立存储的一类内存区域

  • 程序计数器: 可以看成是当前线程所执行的字节码的行号指示器
  • Java虚拟机栈: 生命周期与线程相同,描述的是Java方法执行的内存模型:每个方法子执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息
  • 本地方法栈: 为虚拟机使用到的Native方法服务(而虚拟机栈为Java方法服务)

    线程共享的内存区域

  • Java堆: 在虚拟机启动时创建,用于存放对象实例,可以处于物理上不连续的内存空间,主要逻辑上连续即可
  • 方法区: 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,可以选择笃定大小或者可拓展外,还可以选择不实现垃圾收集
  • 运行时常量池: 方法区的一部分,用于存放编译期生成的各种字面量和符号引用(这部分内容在类加载后进入方法区的运行时常量池存放)

HotSpot虚拟机对象探秘

对象的创建

  1. 虚拟机遇到一条new指令时,检查这个指令的参数是否能在常量池中定位到一个类的符号引用
  2. 为新生对象分配内存
  • 两种分配内存的方法:
    i. 指针碰撞: Java堆中内存绝对规整时
    ii. 空闲列表: Java堆中内存不规整,已使用的内存和空闲的内存相互交错时
  • 并发情况下解决线程安全的两种方法:
    i. 对分配内存空间的动作进行同步
    ii. 把内存分配动作按照线程划分在不同的空间之中进行
  1. 将分配到的内存空间都初始化为零值(对象头除外)
  2. 对对象进行必要的设置(对象头)

    从虚拟机的视角来看,一个新的对象已经产生了,但从Java程序的视角来看,还需要第5步

  3. 执行<init>方法

对象的内存布局

对象在内存中存储的布局可以分为三块区域:对象头、实例数据、对齐填充

  • 对象头: 包括两部分:
    i. 用于存储对象自身的运行时数据,如HashCode、GC分代年龄…
    ii. 类型指针,即对象指向他的类元数据的指针,虚拟机通过这个指针来确定这个对象是那个类的实例(如果对象是一个Java数组,那对象头中还必须有一块用于记录数组长度的数据)
  • 实例数据: 对象真正存储的有效信息,也是在程序代码中定义的各种类型的字段内容
  • 对齐填充: 并不是必然存在的,也没有特别的含义,仅仅起着占位符的作用(HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍)

对象的访问定位

取决于虚拟机的实现,主流的访问方式有两种:

  • 使用句柄: Java堆中会划分出一块内存来作为句柄池,reference中存储的是对象的句柄地址,句柄中包含了对象实例数据与类型数据各自的具体地址信息
  • 直接指针: reference中存储的直接就是对象地址
-------------本文结束感谢您的阅读-------------