jvm阅读01 内存区域

jvm阅读01 内存区域


内存分区

jvm阅读01 内存区域

程序计数器

当前线程所执行字节码的行号指示器,一个处理器只会执行一条线程中的指令,每个线程都有一个程序计数器。
线程执行java方法:计数器内容即java虚拟机当前执行的指令字节码地址;
线程执行native方法:计数器为空。native方法→java调用非java代码的接口。

java虚拟机栈→Java方法执行的内存模型
  • 方法:调用→执行完毕
  • 栈帧(存储变量表,操作数栈,方法出口灯):入栈→出栈
  • 局部变量表存放了 基本数据类型:boolean char byte short int long double float;对象引用:reference类型;
    returnAddress类型(指向下一条字节码指令地址)。局部变量表所需空间在编译器完成分配,也就是说运行期不会改变空间大小。
  • 线程请求栈深度大于虚拟机允许的深度→StackOverflowError;
  • 可扩展虚拟机栈扩展时无法申请足够的内存→OutOfMemoryError。
本地方法栈

作用相似,前者是为执行Java方法服务;后者是为执行native方法服务。

Java堆(heap )
  • Java堆是虚拟机管理的内存中空间最大的一块,作用是存放对象实例,几乎所有的对象实例在这里分配内存。
  • 是垃圾收集器管理的主要区域
  • Java堆可以划分多个线程私有的分配缓冲区(Thread Local Allocation Buffer)TLAB,为了加速内存的分配和回收。
  • 空间可以不连续,逻辑上必须连续,大小可以KUOZHAN1
方法区

存储已经被加载的类信息常量静态变量、即使编译后的代码数据等,Java虚拟机规范吧方法区描述为堆的逻辑部分,但它有个别名叫Non-Heap。

  • 运行常量池 方法区的一部分
    存放编译期生成的各种字面量和符号引用

对象

创建对象

1 、虚拟机收到new指令:检查类加载
  • 检查指令参数能否在常量池中定位到类的符号引用
  • 检查这个符号引用代表的类是否已经被加载,解析和初始化过
  • 没有就要执行类的加载过程
2、为新生对象分配内存:类加载完成就可以确定内存大小
  • 指针碰撞:内存规整,已使用的内存放一边,空闲的放另一边,中间有个指针作为分界点指示器。
  • 空闲列表:内存不规整,虚拟机维护一个列表,记录哪些内存是可用的。
分配空间的线程安全问题
  • CAS+失败重试 保证更新操作的原子性
  • 按照线程在不同空间进行内存分配
    每个线程在Java堆中预先分配一小块内存TLAB,哪个线程要分配内存就在那个线程的TLAB上分配。
3、初始化零值

虚拟机将分配到的内存空间都初始化为零值,保证对象实例字段在java代码中可以不赋初值就进行使用。

4、设置对象

设置对象头

对象布局

对象头:

运行时数据 类型指针
哈希码、GC分代年龄、线程持有的锁、锁状态标志等 虚拟机通过这个指针确定对象是哪个类的实例(不是必须存在的)

实例数据:对象真正存储的有效信息,相同宽的字段总是被分配到一起。

  • longs/doubles ints shorts/chars bytes/booleans opps
  • 父类定义的变量会出现在子类之前

对齐填充:占位符的作用,不是必须存在的,对象大小是8字节(byte)的整数倍。当实例数据没对齐时需要填充。

对象访问定位

通过栈上的reference数据来操作堆上的具体对象。

  • 句柄访问
    优点:对象移动时只会改变句柄中的对象实例数据指针
    缺点:慢
    jvm阅读01 内存区域
  • 直接指针访问
    jvm阅读01 内存区域