【JVM内存管理专题】——JVM内存模型

JVM内存模型——底层逻辑

Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储 到内存和从内存中取出变量这样的底层细节

JVM内存模型——运行结构

在这里插入图片描述
在这里插入图片描述

线程独立—运行内容—操作数栈:栈侦是线程中的代码块序列的基本单位,但却不是执行的基本单位;栈侦中操作栈(基本类型的数据操作)是执行基本单位,
线程独立—运行控制—程序计数器:多线程切换时,作为当前代码执行到何处的标记,可以利用他控制分支、循环、跳转、异常处理,被指向的字节码的地址会送入cpu的CS/IP寄存器来执行,当然这部分代码会被编译成二进制,包括整个字节码地址会被变成一段地址;如果正在执行的是Native方法,这个计数器值则为空(Undefined)。
线程独立—运行设计—动态链接+返回地址:方法会被存储在不同对象中,因此方法调用链需要在不同地址间切换,每个栈侦之间利用动态链接+返回地址实现切换来控制运行的,这些方法的引用会被存储在运行时的常量池中;
线程独立—数据存储(临时)—局部变量表:方法执行期间会生成一些局部变量,这些变量会被存储在一个表中以便于这段时间数据的正常引用(一段代码执行期间,数据能持续访问,除非代码结束),基本类型占用1个Slot;long和double占用2个Sl ot4;因此在递归调用中,局部变量表无限延伸会导致栈溢出这个就是其中亦一样,而非递归方法调用局部变量只会传递引用或者值而不会保存值;使用-Xss参数减少栈内存容量;模拟:可以定义了大量的本地变量,增大此方法帧中本地变量表的长度!
线程共享—数据存储(持久)—堆:堆中创建的数据是所有线程都可以访问到的,java对象会被设计创建在堆中,但是基于逃逸分析技术如果可能创建完就不用的对象直接在栈中可能就被清空了;
线程共享—数据存储(永久)—方法区:一般存储一些元数据,比如类的字节码数据、符号常量池等
直接内存:用于在JDK 1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓 冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储 在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作
避免了在Java堆和Native堆中来回复制数据;服务器管理员,对内存规划需要考虑这部份进去。如果静态取值的话,避免扩展出现限制;

JVM内存模型——对象分配

指针类型

基于不同的虚拟机的设计,在栈中存储的可能是对象的句柄或者是对象的直接地址,有以下区别
对象句柄—易维护:使用句柄来访问的最大好处就是reference中存储的是稳 定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中 的实例数据指针,而reference本身不需要修改;
直接地址—访问快:使用直接指针访问方式的最大好处就是速度更快,它节省了一次指针定位的时间开销, 由于对象的访问在Java中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成 本

内存分配

指针碰撞内存分配:在内存绝对规整的情况下,利用在中间区域的指针说明现在空闲的位置的起始地址,之后的绝对的空闲;
空闲列表内存分配:java内存规划是零散的,就必须维护一个列表说明当前哪些区域是空闲的;
Ps:根据(1)Java堆是否规整决定(2)垃圾收集器是否带有压缩整理---------Serial、ParNew:指针碰撞CMS:采用空闲列表。
指针修改的并发安全问题:指针碰撞中的指针的指向修改是一个线程,而存储数据是一个线程,这个不是原子性的,导致并发安全问题;拿到指针后还没修改前,引用已经指向另外一处,此时该地址是空闲的这个结论已经过期,空闲列表也是如此;JVM如何解决这个问题?
实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性;
另一种是把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,成为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)。哪个线程要分配就在哪个线程的TLAB上分配,只有TLAB用完并分配新的TLAB时,才需要同步锁

JVM内存模型——对象结构

对象头初始-MarkWord

Mark Word的32bit空间中的
25bit用于存储对象哈希码
4bit用于存储对象分代年龄
2bit用于存储锁标志位,在并发编程中,对象作为监视器只是将锁的状态记录在对象头中自由使用;
1bit固定为0
在这里插入图片描述

类加载检查:看看对象的类型在方法栈中是否存在,没有执行类的加载过程,这个时候分配内存的大小可以完全确定;
分配内存位置:jvm在java堆中按照 “指针碰撞” 或者 “空闲列表” 的方式为新生的对象分配内存;
零值初始:虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用地址被征用,程序能访问到这些字段的数据类型所对应的零值。
对象头设置:这个对象是哪个类的实例、如何找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息、这些信息存放于对象的对象头信息
用户初始化:先调用父类然后调用子类

GodSchool
致力于简洁的知识工程,输出高质量的知识产出,我们一起努力
博主私人微信:supperlzf

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值