JVM(Jvm如何管理空间?对象如何存储、管理?)

server/2024/9/22 16:51:41/

Jvm如何管理空间(Java运行时数据区域与分配空间的方式)

⭐运行时数据区域

![[Pasted image 20240419125849.png]]

程序计数器

程序计数器(PC),是一块较小的内存空。它可以看作是当前线程所执行的字节码的行号指示器。Java虚拟机的多线程是通过时间片轮转调度算法来实现的,线程切换后能恢复到正确的执行位置,此时需要程序计数器,各条线程之间计数器互不影响,也即PC是线程独享的。

如果正在执行的是一个Java方法,那么PC记录的就是虚拟机字节码的地址;如果是一个本地方法(Native方法),则PC的值为Undefined。

程序计数器是一个没有被规定任何OutOfMemoryError情况的区域

Java虚拟机栈

Java虚拟机栈也是线程独享,它的生命周期与线程相同。(其实可以理解为一个线程就是一个栈)

虚拟机栈是Java方法执行的线程内存模型:当方法被执行时,Java虚拟机就会同步生成有个栈帧(用于存储局部变量,操作数栈,动态链接,出口等),每一个方法从被调用到执行完毕就对应一个栈帧入栈和出栈的过程。栈帧中用于存储局部变量表、操作数栈、动态连接、方法出口等信息。

栈帧(参考链接):

  • 局部变量表:是用来存储我们临时8个基本数据类型、对象引用地址、returnAddress类型。(returnAddress中保存的是return后要执行的字节码的指令地址。)
  • 操作数栈:操作数栈就是用来操作的,例如代码中有个 i = 6*6,他在一开始的时候就会进行操作,读取我们的代码,进行计算后再放入局部变量表中去
  • 动态链接:假如我方法中,有个 service.add()方法,要链接到别的方法中去,这就是动态链接,存储链接的地方。
  • 出口:出口是什呢,出口正常的话就是return 不正常的话就是抛出异常落。
方法区

方法区是所有线程共享的内存区域,它用于存储已被Java虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。它有个别命叫Non-Heap(非堆)。当方法区无法满足内存分配需求时,抛出OutOfMemoryError异常。

Java堆

Java堆是java虚拟机所管理的内存中最大的一块,是线程共享的,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例java堆是垃圾收集器管理的主要区域,因此也被成为“GC堆”

从内存回收角度来看java堆可分为:新生代和老生代。从内存分配的角度看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区(TLAB)。无论是哪个区域,存储的都只能是对象的实例,将Java堆细分的目的只是为了更好地回收内存,或者更快地分配内存。如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,Java虚拟机将会抛出OutOfMemoryError异常。

本地方法栈

他很栈很像,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务

⭐空间分配的方式

  • 指针碰撞法:假设Java堆中内存是绝对规整的,所有被使用过的内存都被放在一边,空闲的内存被放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间方向挪动一段与对象大小相等的距离,这种分配方式称为“指针碰撞”。
  • 空闲列表法:但如果Java堆中的内存并不是规整的,已被使用的内存和空闲的内存相互交错在一起,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为“空闲列表”。

⭐Jvm中的对象是如何存储、管理的?

对象组成

在HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:对象头、实例数据和对齐填充。

  • 对象头
    • Mark Word:Mark Word一个有着动态定义的数据结构,用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
    • 类型指针:即对象指向它的类型元数据的指针。
    • 数组长度:如果对象是一个Java数组,对象头中还必须有一块用于记录数组长度的数据
  • 实例数据:对象真正存储的有效信息
  • 对齐填充:这并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于在HotSpot虚拟机里任何对象的大小都必须是8字节的整数倍。如果对象实例数据部分没有对齐的话,就需要通过对齐填充来补全,来使得对象头是8字节的整数倍。

如何访问一个对象?

Java程序会通过栈上的reference数据来操作上的具体对象。主流的访问方式主要有使用句柄和直接指针两种。

句柄

使用句柄来访问的最大好处就是reference中存储的是稳定句柄地址,当对象被移动时,reference本身不需要被修改,只需要修改句柄池中的实例数据指针。
![[Pasted image 20240425165035.png]]

直接指针

直接指针来访问最大的好处就是速度更快,它节省了一次指针定位的时间开销,由于对象访问在Java中非常频繁,因此这类开销积少成多也是一项极为可观的执行成本。
![[Pasted image 20240425165043.png]]


http://www.ppmy.cn/server/23893.html

相关文章

VUE的生命周期图和各函数

函数 beforeCreate(){ }, created(){ }, beforeMount(){ }, mounted(){ }, beforeUpdate(){ }, updated(){ }, beforeDestroy(){ }, destroyed(){ } 创建时生命周期图 运行时生命周期图

【MHA】MySQL高可用MHA源码1-主库故障监控

1 阅读之前的准备工作 1 一个IDE工具 ,博主自己尝试了vscode安装perl的插件,但是函数 、变量 、模块等都不能跳转,阅读起来不是很方便。后来尝试使用了pycharm安装perl插件,阅读支持跳转,自己也能写一些简单的测试样例…

飞行汽车飞行控制系统功能详解

飞行汽车是一种创新的交通工具,结合了汽车和飞机的特点。它可以在陆地上行驶,同时也具备在空中飞行的能力。飞行汽车的概念已经存在多年,并且近年来随着技术的进步和研发的深入,这种交通工具正在逐渐从概念走向现实。 飞行汽车的…

LeetCode-非递增子序列

每日一题 今天刷的依旧是一道中等题,不过感觉今天这道题是中等难度里面比较难的题了,思考了很长时间。过程感觉比较难以理解。 题目要求 给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两…

第27篇 Spring简介

Spring框架是Java企业级应用的主流框架,其主要基于IoC(Inversion of Control,控制反转)和DI(Dependency Injection,依赖注入)设计原则。Spring的核心语法主要包括Bean的定义、装配、自动扫描、A…

利用GaussDB的可观测性能力构建故障模型

D-SMART高斯专版已经开发了几个月了,目前主要技术问题都已经解决,也能够初步看到大概的面貌了。有朋友问我,GaussDB不已经有了TPOPS了,为什么你们还要开发D-SMART高斯专版呢? 实际上TPOPS和D-SMART虽然都可以用于Gaus…

前端vue scope的定义以及用法

这段代码是 Vue 组件中用于定义表格列的代码,包含了自定义模板和逻辑,以显示特定格式的内容。在这里,el-table-column 来自 Element UI 框架,提供了一种简洁的方式来定义表格的列及其显示内容。 让我们看看这段代码的细节&#x…

pytorch 实现语义分割 PSPNet

语意分割是指一张图片上包含多个物体,通过语义分割可以识别物体分类、物体名称、像素识别的任务。和物体检测不同,他不会将物体框出来,而是根据像素的归属把物体标注出来。PSPNet 的输入是一张图片,例如300500,那么输出…