深入理解Java虚拟机(1)——内存区域

目录

前言

一、运行时数据区域

1.程序计数器(Program Counter Register)

2.虚拟机栈(VM Stack)

3.本地方法栈(Native Method Stack)

4.Java堆(Heap)

5.方法区(Method Area)

二、直接内存

1.DirectByteBuffer

2.操作

3.异常

三、对象

1.创建对象

2.内存布局

3.访问定位


前言

参考周志明的《深入理解Java虚拟机》,进行简单的总结记录。本小结总结了《深入理解Java虚拟机》第二章内存区域。


一、运行时数据区域

1.程序计数器(Program Counter Register)

  • 线程私有。
  • 针对方法内部:字节码调用。
  • 可以看作是当前线程执行的字节码的行号指示器。控制着程序运行的顺序、分支、循环、跳转等等。
  • 执行Java方法:记录的是字节码指令的地址。
  • 执行Native方法:记录的是空(undefined)。
  • 内存中唯一没有OOM的区域。

2.虚拟机栈(VM Stack)

  • 线程私有。
  • 描述的是Java方法执行时线程的内存模型,直观上看就是debug调试的堆栈信息。
  • 一个方法从调用到被执行完毕,就是对应一个栈帧从入栈到出栈的过程。
  • 栈帧保存了局部变量表、操作数栈、动态链接、方法出口等信息。
  • 异常:线程请求栈的深度超过虚拟机允许深度,发生StackOverFlowError;虚拟机栈动态扩展时无法申请到内存,发生OutOfMemoryError。

3.本地方法栈(Native Method Stack)

  • 与虚拟机栈类似,只不过是为Native方法服务。

4.Java堆(Heap)

  • 线程共享
  • 虚拟机中很大的一块内存,保存对象的实例。
  • 垃圾回收的主要区域。
  • 异常:堆内无法实例分配,且堆无法扩展,发生OOM。

5.方法区(Method Area)

  • 线程共享
  • 堆的一个逻辑部分,别名:Non-Heap
  • 存储内容:虚拟机加载的类、常量、静态变量、即时编译器编译后的代码。
  • HotSpot虚拟机对方法区的实现叫做永久代,jdk1.8中,移到了元空间(MetaSpace)。
  • 方法区内还有一块内存叫做运行时常量池,存储已被加载的class文件中的常量池表(Constant Pool Table)。当常量池无法申请到内存时,发生OOM。

二、直接内存

1.DirectByteBuffer

  • 占用堆外内存,是Native直接调用的机器内存。JVM会在DirectBuffer上直接使用本地IO操作(Native I/O Operations)。
  • 好处:避免额外的数据复制,提高IO效率。减低GC的工作量。
  • 场景:需要容量较大且长期使用的缓存。需要测试确定是否能获得性能提升。
  • MappedByteBuffer:继承DirectByteBuffer,可以将文件内容映射到内存。

2.操作

  • 通过存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。
  • DirectBuffer一般在FullGC时被回收。

3.异常

  • 不受参数-Xmx参数控制,设置大小:-XX:MaxDirectMemory=512M
  • 受物理内存、SWAP分页限制。
  • 如果堆内存Xmx满了,物理内存和分页也用完了,则无法继续申请直接内存(OOM)。

三、对象

1.创建对象

  • 内存划分:指针碰撞空闲列表
    • 指针碰撞:内存规整,只需要移动指针来分配空间。
    • 空闲列表:内存不规整,虚拟机需要维护哪些空间是空闲的,找到足够的空间分配。
  • 如何选择:取决于Java堆是否规整,Java堆是否规整又取决于GC是否带有空间压缩整理的能力。

2.内存布局

  • 对象头Header:Mark Word+Class pointer+array length
    • Mark Word:存储对象自身的运行时数据,如hashcode,gc分代年龄、锁标志等。
    • Class pointer:类型指针,指向它的类元数据,JVM通过这个指针确定对象时哪个类的实例。
    • array length:如果对象是数组,那么对象头还需要额外的空间存储数组的长度。
  • 实例数据:对象真正存储的有效数据。
  • 对其填充:非必须,占位功能,因为hotspot自动内存管理要求对象起始地址必须是8字节的整数倍,如果不是,则用它填充。

3.访问定位

  • 句柄池:Java堆中划出一块内存存储句柄池,引用存储的句柄池的地址,而句柄中存储了对象实例数据和到对象类型数据的指针。
    • 优点:稳定,当对象移动(因为gc等)时只需要修改句柄中到对象实例的指针即可。
    • 缺点:两次寻址
  • 直接指针:reference中存储的就是对象的地址,此时对象中就要存储到对象类型数据的指针了。(HotSpot主要使用)
    • 优点:访问对象比较快。

你可能感兴趣的:(笔记,java,jvm)