java内存管理机制(二)-内存分配

在上一篇文章中,我们花了较大的篇幅去介绍了JVM的运行时数据区,并且重点介绍了栈区的结构及作用,在本文中,我们将主要介绍对象的创建过程及在堆中的分配方式。

对象的创建

在上文我们提过一些问题,你的对象是怎么new出来的?new出来又放在哪里?怎么引用的? 老规矩,我们还是通过字节码来了解一下。

public static void main (String[] args){People p = new People();
}

这样的代码大家一点也不会陌生,我们都知道使用new关键字可以创建一个对象,咦!一看字节码才知道,我们的一行new的代码,对应的字节码原来要做这么多操作!我们逐一来分析一下。

一、new指令

1、检查、加载相应类

JVM遇到new指令时,先检查指令参数(上面字节码中的#2)是否能在常量池中定位到一个类的符号引用(上面最终定位到常量池中的com/test/entity/People):

  1. 如果能定位到,检查这个符号引用代表的类是否已被加载、解析和初始化过;
  2. 如果不能定位到,或没有检查到,就先执行相应的类加载过程;

具体类的加载、解析、初始化的过程大家可以去查找JVM类加载机制相关资料,这里就不展开啦!我们需要知道的是这一步保证了在方法区中,存在要创建实例对象的类对象

 2、空间分配

咱们到了适婚年龄,也就该找个对象了吧!你看上了一个姑娘,长得楚楚动人,就跑去跟他妈说:“我要一个对象,把你女儿嫁给我吧!”。她妈妈倒是十分爽快:“好啊,我女儿总得有个地方住吧,小伙子你有房吗?”。这时候场面一度十分尴尬,心里嘀咕着“要是国家能分配房子就好了!”。这在当前社会显然不现实,毕竟咱们还没进入共产主义社会!然而在JVM王国里,对象住的“房子”却是“国家”统一分配的。国家集中圈了一大块“地”,谁家要娶“媳妇”,就给他家分配一块“地”,“媳妇”胖点呢,地就大一点,“媳妇”瘦一点呢,“地”就小一点。在这里,你一个人可以同时拥有多个对象,在这里,多个人可以拥有同一个对象。所以这里的老百姓安居乐业、这里一片祥和……当然,由于这块“地”大小有限,而你又同时拥有很多对象,还有其他人也要娶对象,所以那些不用了的对象的“地”国家就会进行统一征收(当然这里不会给补贴,毕竟是免费分配的~)以继续分给其他人用。

上面扯了这么多,相信你已经知道“你”就代表着一个线程,“国家”指的是JVM,“国家”圈的一块“地”就是堆空间,你娶的“对象”就是实例对象,“国家”分配地的动作就是内存分配,而国家征收的动作就是垃圾回收。

由于要找对象的人太多了,所以分配的操作也很频繁,那么摆在“国家”的问题就来了:怎么合理分配?怎么最大限度的提高空间利用率?怎么提高分配效率?不用了的空间怎么回收?怎么知道哪些空间不用了?上面很多问题都需要结合后面的垃圾回收相关的内容来讨论,这里只讨论分配内存的方式。

一个对象需要占用多大的内存?这个问题其实在类加载完成后就已经确定啦!JVM可以通过普通java对象的类元信息确定对象大小。为对象分配内存相当与把一块确定大小的内存从java堆中划分出来。那么问题来了,这么大的一块堆空间摆在JVM的面前,JVM该划哪一块空间来分配内存呢?随机找一块空间分配算了?or紧挨着之前分配的空间后面进行分配?这里需要说到的是两种分配方式:

1)、 指针碰撞

如果Java堆是绝对规整的:一边是用过的内存,一边是空闲的内存,中间一个指针作为边界指示器,分配内存只需向空闲那边移动指针,这种分配方式称为"指针碰撞"(Bump the Pointer)。这里有个条件就是“绝对规整”,类似下图,左边全是被绿过了的,右边则全是等着被绿的。新分配对象时候就是多绿了一块,边界指示器向后移动!

2)、 空闲列表

如果Java堆不是规整的:用过的和空闲的内存相互交错。需要维护一个列表,记录哪些内存可用。分配内存时查表找到一个足够大的内存,并更新列表,这种分配方式称为"空闲列表"(Free List)。类似下图,好好的一块内存被绿得乱七八糟,用上面指针碰撞的方式是碰不动了!所以就用一个小本本记着哪里有多大的空闲空间可以绿!当然下图的地址编号是虚拟的,空闲列表的样子也是我意淫出来的,表达的意思你懂就行!

我们能看到,导致这两种方式的差异主要取决于java堆是否规整,而java堆是否规整又是由jvm采用的垃圾收集器是否带有压缩功能决定的。使用Serial、ParNew等带Compact过程的收集器时,JVM采用指针碰撞方式分配内存。而使用CMS这种基于标记-清除(Mark-Sweep)算法的收集器时,采用空闲列表方式。(下篇文章会具体介绍不同的垃圾收集器)

不管是指针碰撞还是空闲列表,都会存在同一个问题,那就是在多线程的场景下的线程安全问题。多个线程同时在new的时候把对象分配到同一块内存了咋办,不得干起来么!于是jvm采用了两种方案来解决:

1)、 同步处理:JVM采用CAS(Compare and Swap)机制加上失败重试的方式,保证更新操作的原子性。CAS机制是一种轻量级锁机制,后续在聊多线程的时候再讲!

2)、 本地线程分配缓冲区:把分配的内存按照不同的线程划分在不同的空间进行,每个线程在java堆区预先分配一小块内存,称为本地线程分配缓冲区(Thread Local Allocation Buffer)。哪个线程需要分配就从哪个线程的TLAB上分配,只有在TLAB用完需要分配新的TLAB的时候才需要做同步处理(通过上一点中的CAS机制)。

3、对象初始化

内存分配完后,就需要初始化实例对象了,虚拟机需要将分配到的内存空间中的数据类型都初始化为零值(不包括对象头,如果是使用TLAB,初始化0值的操作提前至分配TLAB时)。接下来虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息,这些信息都存放在对象的对象头中。做完以上以后,从虚拟机视角来看,一个新的对象已经产生了!

4、返回地址

JVM完成对象内存的分配及对象初始化之后,会返回对象的地址,并且压入操作数的栈顶,供后续操作!

二、dup指令

dup命令没猜错的话是duplicate的简写。在讨论dup命令前,我们先看一个简单的例子

public static void main (String[] args){int a;int b = a = 88;}

我们看看对应的字节码

public static void main(java.lang.String[]);
Code:
0: bipush 88
2: dup
3: istore_1
4: istore_2
5: return

由于88这个值在一条语句中需要重复赋给两个变量,所以使用dup指令对栈顶的值进行了复制,且压入栈顶。我们在new对象的时候,new指令后面都会紧跟dup指令!然后是invokespecial和astore指令,相信聪明的你应该想到invokespecial和astore指令都会需要从栈顶弹出值来执行!在执行完dup指令后,操作数栈栈顶就有两个指向该对象实例内存的reference数据,如果<init>方法有参数,还需要把参数加载到操作栈。

三、invokespecial指令

invokespecial指令调用对象实例方法<init>,通过符号引用#3定位到的是People对象的实例方法<init>。这时候操作数栈栈顶值(指向对象实例的内存reference)会被弹出(如果<init>方法有参数,参数也会出栈)。执行<init>方法会在java虚拟机栈中创建<init>方法的栈帧,并且把出栈的数据放入栈帧的局部变量表中。变量表中指向对象实例的内存reference就是我们经常用到的this,表示对该对象实例进行操作!执行完该指令后,一个完整的对象就创建完成啦!

四、astore指令

astore依然需要弹出栈顶值,然后存储到编号为1的变量中供后续使用。至此一个完整的对象已经创建且返回对象内存引用给本地变量存储了。

对象的访问定位

我们上面已经把对象创建的问题解决了,同时我们也都知道,引用类型的变量存储的是**对象的引用**!那这个引用类型数据怎么定位到堆中的对象呢?目前主流的对象访问方式有两种:

1、使用句柄

JVM在堆区划分一块内存作为句柄池,引用类型变量中存储就是对象的句柄地址。对象句柄包含两个地址(如下图):

  • 在堆中分配的对象实例数据的地址。
  • 这个对象类型数据地址。

2、使用直接指针

引用类型变量中存储就是在堆中分配的对象实例数据的地址。

句柄池的方式会在句柄池中存放类型对象的相关信息,而直接访问的方式会把类型对象的信息放入实例对象的对象头中(我们知道对象头包含“指向对象类型数据的指针”,其实这并不是必须的,我们常用的HotSpot虚拟机采用的是直接指针的方式,所以对象头中会包含“指向对象类型数据的指针”,如果某类虚拟机采用的是句柄的方式访问对象,那可能就不需要在头部存储这个指针了)。这两种方式都互有优缺点:

  1. 句柄方式访问对象时,多一次指针定位的时间开销。但是对象移动时(垃圾回收时常见的动作),栈上的变量的引用不需要修改,只需改变句柄中实例数据指针。
  2. 直接指针对象相对句柄方式访问节省了一次指针定位的时间开销,性能更好。如果对象访问非常频繁,提升会更明显!但是在对象移动时,栈上的变量的引用也需要变化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/39749.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

5.3.3、二维数组案例-考试成绩统计

代码 #include <iostream> using namespace std; #include <string>int main() {//二维数组案例-考试成绩统计//1、创建二维数组int scores[3][3] {{100,100,100},{90,50,100},{60,70,80},};string names[3] { "张三","李四","王五&quo…

基于星火大模型的群聊对话分角色要素提取挑战赛|#AI夏令营#Datawhale#夏令营-Lora微调与prompt构造

赛题连接 https://challenge.xfyun.cn/topic/info?typerole-element-extraction&optionphb Datawhale Al夏令营 零基础入门大模型技术竞赛 数据集预处理 由于赛题官方限定使用了星火大模型&#xff0c;所以只能调用星火大模型的API或者使用零代码微调 首先训练数据很少…

周志华机器学习西瓜书经典来袭!PPT+课件+南瓜书

南京大学教授&#xff0c;博士生导师&#xff0c;教育部“长江学者”特聘教授&#xff0c;国家杰出青年基金获得者&#xff01;周志华老师的头衔太多了&#xff01;相信每一个从事或爱好机器学习的朋友都知道周志华老师&#xff0c;尤其是他的那本经典巨作《机器学习》&#xf…

慧哥Saas充电桩开源平台 V2.5.5

文章目录 原地址&#xff1a;https://gitee.com/chouleng/cdzkjjh&#xff0c;更换新的地址如下 [点击此链接 https://gitee.com/chouleng/huili-cloud](https://gitee.com/chouleng/huili-cloud)一、产品功能部分截图1.手机端&#xff08;小程序、安卓、ios&#xff09;2.PC端…

华为云OBS 通过S3客户端访问

华为云好像没有对S3协议的支持说明其实底层是支持S3协议的。 使用S3的时候我们会需要endpoint&#xff0c;桶名字&#xff0c;region&#xff0c;AWS_ACCESS_KEY,AWS_SECRET_KEY 其中endpoint 就是图片中的&#xff0c;桶名字也很容易找到&#xff0c;region 就是你的endpoint…

博途TIA Portal「集成自动化软件」下载安装,TIA Portal 灵活多变的编程环境

在编程领域&#xff0c;博途TIA Portal以其卓越的编程工具和灵活多变的编程环境&#xff0c;为众多用户提供了前所未有的便利。这款软件不仅支持多种编程语言&#xff0c;如梯形图&#xff08;Ladder Diagram&#xff09;、功能块图&#xff08;Function Block Diagram&#xf…

华为HCIP Datacom H12-821 卷24

1.单选题 企业大楼有大量员工通常都在上班时在大厅开始接入到公司的WLAN网络,随着每位员工走到各自的工位过程中&#xff0c;每个人的移动端叶通过漫游的方式漫游到各自的网络覆盖区域。为了尽量保证每个终端的IP地址是固定的&#xff0c;建议的做法是? A、配置VLAN Poo…

统计信号处理基础 习题解答11-13

题目 如果是一个2x1的随机矢量&#xff0c;具有PDF 证明的PDF是一个随机变量。提可以因式分解成&#xff0c;其中是一个在4.5节描述的白化变换。 解答 首先&#xff1a; 因此&#xff0c;存在&#xff1a; 也就是是Hermitian矩阵。详细的性质可以参考&#xff1a; https://z…

抠图怎么抠?教你3种一看就会的抠图工具

抠图怎么抠&#xff1f;抠图&#xff0c;作为图像处理中的一项基本而强大的技能&#xff0c;广泛应用于摄影后期、广告设计、影视特效等多个领域。它能够将图像中的特定对象或区域从背景中精确分离出来&#xff0c;便于后续编辑或与其他图像合成。随着科技的发展&#xff0c;现…

个人PayPal账户与企业PayPal账户:差异与选择

PayPal作为全球领先的在线支付平台&#xff0c;为不同用户群体设计了两种类型的账户&#xff1a;个人账户和企业账户&#xff0c;不仅为个人用户提供了便捷的支付和收款服务&#xff0c;同时也为企业用户提供了丰富的电子商务解决方案&#xff0c;让个人和企业都能使用便捷的电…

实现高效写入:Schemaless 写入性能优化指南

物联网应用常常需要收集大量的数据&#xff0c;用以支持智能控制、业务分析和设备监控等功能。然而&#xff0c;应用逻辑的更新或硬件的调整可能会导致数据采集项频繁变化&#xff0c;这是时序数据库&#xff08;Time Series Database&#xff0c;TSDB&#xff09;面临的一大挑…

vue中自定义设置多语言,并且运行js脚本自动生成多语言文件

在项目中需要进行多个国家语言的切换时&#xff0c;可以用到下面方法其中一个 一、自定义设置多语言 方法一: 可以自己编写一个设置多语言文件 在项目新建js文件&#xff0c;命名为&#xff1a;language.js&#xff0c;代码如下 // language.js 文档 let languagePage {CN…

红酒与舞蹈:舞动的味觉艺术

在艺术的海洋中&#xff0c;红酒与舞蹈总是能激起人们心中较温柔的涟漪。红酒以其深邃的色泽、馥郁的香气&#xff0c;诠释着味觉的艺术&#xff1b;而舞蹈&#xff0c;则以优雅的姿态、灵动的步伐&#xff0c;演绎着视觉的盛宴。当红酒遇上舞蹈&#xff0c;一场别开生面的艺术…

少见的更优写法,反转字符串中的元音字母

Leetcode 原题链接 解法一 这道题很简单&#xff0c;令双指针 l l l 和 r r r 从两侧相向移动&#xff0c;交换元音字母即可。但大多人的实现是如下这种可简化的嵌套循环。 如果是 Java 等 String 不可变的语言&#xff0c;应先转换为 CharArray&#xff0c;交换完元音字母…

家用洗地机什么牌子好?四款公认品牌好的机型推荐

每个人都希望自己的家里面能够干干净净&#xff0c;就算不是一尘不染&#xff0c;也至少应该是整洁的&#xff0c;但是在这个快节奏的大环境下&#xff0c;做清洁对于人们来说&#xff0c;不是没时间&#xff0c;就是太累了。正当此时&#xff0c;一款造福懒人的神器——家用洗…

4D 生物打印技术的挑战:从打印到植入,还有多远?

4D生物打印技术将时间维度融入生物打印&#xff0c;为构建具有动态特性和功能的生物组织结构提供了无限可能。然而&#xff0c;要实现这些目标&#xff0c;选择合适的生物打印技术至关重要。本文将详细介绍几种主要的4D生物打印技术&#xff0c;并分析它们各自的优缺点&#xf…

前端初学java二(类、多态、接口、内部类、泛型)

目录 类 种类 Javabean类 测试类 工具类 类的初始化 构照函数 新建对象的内存图 static 继承 This Super 虚方法表 Override 修饰符权限 构造代码块 静态代码块 多态 前提 优点 缺点 示例 抽象方法 抽象类 接口 implements 继承 内部类 成员内部类…

React+TS 从零开始教程(4):useEffect

上一节传送门&#xff1a;ReactTS 从零开始教程&#xff08;3&#xff09;&#xff1a;useState 源码链接&#xff1a;https://pan.quark.cn/s/c6fbc31dcb02 上一节&#xff0c;我们已经学会了React的第一个Hook&#xff1a;useState。 这一节&#xff0c;我们要学习的是另一…

C语言----文件操作

1.为什么使用文件&#xff1f; 如果没有⽂件&#xff0c;我们写的程序的数据是存储在电脑的内存中&#xff0c;如果程序退出&#xff0c;内存回收&#xff0c;数据就丢失了&#xff0c;等再次运⾏程序&#xff0c;是看不到上次程序的数据的&#xff0c;如果要将数据进⾏持久化…

Java语言开发的一套智慧产科系统源码:产科专科电子病历系统源码

Java语言开发的一套智慧产科系统源码&#xff1a;产科专科电子病历系统源码 系统概述 电子病历系统是以住院病人为中心&#xff0c;面向医生以及护士为主的&#xff0c;涉及临床治疗、护理等业务的临床信息系统&#xff0c;以电子信息技术为手段&#xff0c;实时采集病人在整个…