JVM内存结构

运行时数据区

内存概览图

image-20220717173811805

程序计数器

这里的程序计数器也就如同操作系统中的程序计数器,可以看作用于指定当前线程所执行的字节码。在Java虚拟机中,一个处理器内核只会执行一条线程的指令。各条线程之间的程序计数器互不影响,独立存储。

如果当前线程正在执行Java方法,则计数器指向虚拟机字节码的内存地址。如果执行Native方法,则计数器为空

虚拟机栈

虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行时,虚拟机会同步创建一个栈帧用于存储局部变量表,操作数栈、动态连接、方法出口等。

方法的调用与完成对应着栈帧在栈空间中的入栈与出栈

模型如下:

image-20220717210158519

异常信息:

  • 当线程请求的栈深度大于虚拟机允许的深度,将抛出StackOverflowError
  • 如果栈容量可以动态扩展,当栈需要扩展且无法申请到足够的内存将抛出OutOfMemoryError

本地方法栈

为虚拟机使用到的本地方法服务

异常信息同上

Java堆几乎是所有对象实例分配的空间。也是被GC管理的内存区域

在线程共享的堆内存中也可以分配出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)

主流Java堆内存大小是可扩展的(通过参数-Xmx:最大,-Xms:最小;来进行设定)

如果堆无法再扩展,则将抛出OutOfMemoryError异常

方法区

用于存储已经被虚拟机加载的类型信息常量,静态变量,及时编译器编译后的代码缓存等数据

《Java虚拟机规范》并未对方法区做严格的实现规范。

image-20220717215929056

运行时常量池

运行时常量池是方法区的一个部分。Class文件中的常量池表用于存放编译期生成的各种字面量与符号引用。而这部分内容将存放到运行时常量池

此外,运行时常量池还会存储由符号引用翻译出来的直接引用

直接内存

在JDK1.4中加入了NIO,可以使用Native函数库直接分配堆外内存。

HotSpot虚拟机对象

对象创建

使用new关键字创建对象时

  1. 检查指令的参数能否在常量池中定位到类的符号引用,(并检查这个类是否已被加载、解析和初始化)
  2. 为新生对象分配内存,
    1. 指针碰撞——所有被使用过的内存放一边,空闲内存放另一边,中间用指针分隔
    2. 空闲列表——内存不规整,需要维护一个空闲内存列表,分配时从列表中找到足够的空间分配给对象

此外,由于对象创建非常频繁,在并发情况下:可能出现正在给对象A分配内存,指针还未修改,对象B又同时使用原来的指针进行内存分配。

解决上述问题又两种可选方案:①对分配内存空间操作进行同步处理(CAS+失败重试)②把内存分配动作按照线程划分在不同的空间中,每个线程在堆中预先分配一块内存,称为Thread Local Allocation Buffer(TLAB),只有当线程的本地缓冲区用完,分配新缓冲区时同步。可使用-XX:+/-UseTLAB指定是否启用TLAB

  1. JVM需要对对象进行必要的设置

对象内存布局

对象在堆内存存储布局可划分为:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)

Header

Header存储两类信息:

第一类:存储对象运行时数据,如:哈希码,GC分代年龄、锁状态、偏向线程ID、偏向时间戳

第二类:类型指针,JVM通过指针确定对象是哪个类的实例

在32位机上占32字节

Instance Data

在程序中定义的各种字段,字段内存分配原则:相同宽度字段在一起,在这个前提下,弗雷定义的变量会在子类之前

Padding

虚拟机要求任何对象大小必须是8字节整数倍

对象的访问

主流引用访问的方式有:

  • 使用句柄

    image-20220723111141232

  • 使用直接指针访问

    image-20220723111042105

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值