首页 > 其他分享 >从零开始理解JVM:对象的生命周期之对象创建

从零开始理解JVM:对象的生命周期之对象创建

时间:2024-11-26 23:32:05浏览次数:5  
标签:对象 从零开始 线程 内存 JVM 标量 分配 指针

一、对象创建

从你new一个对象开始,发生了什么?

遇到new指令,jvm首先要做的事是检查有没有这个类,没有的话,加载它!

接下来,就要进行实例的内存分配,通过什么样的方式进行内存分配呢?

1、内存分配方式

指针碰撞

这种分配前提是内存中有整片连续的空间,用的在一边,空闲的在另一边,用一个指针指向当前已经被分配的内存边界。

需要多少指针往空闲那边移动多少,直接划分出来一段,给当前对象,完工。

分配内存就由指针往后挪就行了

但是这种方式,思考一个问题,中间的一些对象被回收之后,为了确保内存的连续性,是不是该把后面的对象占用的内存往前移,这称为内存整理,显然,是有很大开销的,如果在一个区域发生对象回收的频率较高,用指针碰撞的方式是不适合的。

空闲列表

那如果jvm堆不那么规整呢?用的和没用的交叉在一起,也就是我们所说的内存碎片。

这种情况就需要我们单独有一张表来记录,哪些内存块是空的。

分配的时候查表,找到大小够用的一块,分配给对象,同时更新列表。

2、并发性问题

无论指针移动还是空闲列表的同一个指针空间,在并发分配的情况下会不会有问题?

很聪明!确实有并发问题。那jvm是如何解决的呢?(当然传统的粗暴的加锁和同步机制肯定能解决,我们暂不讨论这个)

方式一:cas原子操作 + 失败重试

在做内存指针更新的时候,将指针的获取和更新操作变为一气呵成的原子操作

CAS 操作通常包括三个参数:内存位置(V)、预期值(A)和新值(B)。CAS 操作会检查内存位置 V 的值是否等于预期值 A,如果是,则将 V 的值更新为 B;否则,操作失败。

操作失败就进行重试就行了

方式二:本地线程分配缓冲(TLAB)

TLAB 是 JVM 为每个线程分配的一个本地缓冲区(其实就是从堆上分配一小块空间给每个线程),用于对象的快速分配。每个线程在其 TLAB 中分配对象,这样可以减少线程之间的竞争,提高内存分配的效率。

那么线程创建对象需要内存时,可以在自己划走的堆上先操作。相当于每个线程批发了一批内存先用着。

当前线程空间不够时,再去公共堆上申请,这样就减少了并发冲突的机会。当然也多少有点浪费

3、对象分配内存的完整过程总结

我们要先讲两个概念,逃逸分析和标量替换

逃逸分析:

逃逸分析是一种编译器优化技术,用于确定对象的作用域。如果一个对象只在一个方法内部使用,且不会被其他线程访问,那么这个对象就被称为“未逃逸”。

对于未逃逸的对象,可以进行“标量替换”。

标量替换:

标量替换是一种优化技术,它将对象拆解成多个标量值(如基本类型或引用),并将这些标量值直接存储在栈上,而不是创建一个完整的对象。避免了对象的创建和垃圾回收。

接下来我们再来看完整的对象分配内存过程:

①,若开启逃逸分析,那么对于未逃逸的对象,我们将直接在本地的栈帧中分配内存(当然是内存空间够的情况下),这个过程也利用标量替换来优化

②,如果本地栈空间不够,若采用TLAB,我们会优先在TLAB中分配

③,若TLAB空间也不够,我们才会在堆区进行分配内存,大概率是进入Eden区

二、内存布局

上面我们给这个对象分配好了内存空间,那么问题来了。对象拿走的这块内存,它都写了些啥进去呢?

对象在堆上的布局,可以分为三个部分:对象头、实例数据、对齐填充。

1、对象头

对象头一般分为两部分,Mark Word 和 类型指针(Hotspot)

1)Mark Word,官方叫法,其实就是存储对象自己运行时的数据

如哈希码、GC分代年龄、锁状态标记、线程持有的锁、偏向的线程id……(不用记)

2)类型指针(Klass)

指向当前对象的类型。也就是方法区里,类信息的地址。

当然这里不是绝对的,hotspot这么设计。

2、实例数据

对象里各个字段的值。这个好理解。

long,double,int等长度都是固定的

string、对象类型等是个地址,指向其他外部堆空间

3、对齐填充 

不是必须的。就是个占位符而已。

Hotspot规定的,内存管理系统要求对象的大小必须是8字节的整数倍。

三、对象的访问

句柄访问

句柄方式:

栈指针指向堆里的一个句柄的地址,这个句柄再定义俩指针分别指向类型和实例

很显然,垃圾回收移动对象的话只需要改句柄即可,不会波及到栈,但是多了一次寻址操作

直接地址

直接地址:

栈指针指向的就是实例本身的地址,在实例里封装一个指针指向它自己的类型

很显然,垃圾回收移动对象要改栈里的地址值,但是它减少了一次寻址操作。

备注:hostspot使用的是直接地址方式

标签:对象,从零开始,线程,内存,JVM,标量,分配,指针
From: https://blog.csdn.net/qq_46248151/article/details/144060911

相关文章

  • Java基础-scanner对象
    scanner对象next():一定要读取到有效字符后才可以结束输入对输入有效字符之前遇到的空白,next()方法会自动将其去掉只有输入有效字符后才将其后面输入的空白作为分隔符或结束符next()不能得到带有空格的字符串//创建一个扫描器对象,用于接收键盘数据Scannerscanner=......
  • java面向对象知识点: 封装,构造,重载
    目录封装封装知识点private(私有)public(公共) 二、getter和setter方法getter方法(访问器方法)setter方法(修改器方法)三、封装类的设计原则单一职责原则高内聚性一.Java为什么要封装?一、数据隐藏与安全性保护数据不被随意访问防止外部干扰二、提高代码的可维......
  • java JVM的深入理解
    javaJVM的理解:编译--》运行(利用虚拟机【解释器】解释执行class字节码文件1.JVM的位置:JVM存在JRE里,是java的运行环境;2.JVM的体系结构java--》.class--》类装载器--》运行时数据区(方法区,Java栈,本地方法区navtivemethod堆,程序计算器)--》执行引擎3.类加载器:加载class文件,启动......
  • 前端技术中对JavaScript对象的学习
    对象目录对象创建对象使用循环遍历对象属性对象中的方法创建对象创建新对象有两种不同的方法:使用Object定义并创建对象的实例。使用函数来定义对象,然后创建新的对象实例。newObject在JavaScript中,几乎所有的对象都是Object类型的实例,它们都会从Object.prototype......
  • Prometheus Operator自定义监控对象 -- Ingress-Nginx
    PrometheusOperator自定义监控对象--Ingress-Nginx一、自定义资源Prometheus-operator通过定期循环watchapiserver,获取到CRD资源(比如servicemonitor)的创建或者更新,将配置更新及时应用到运行中的prometheuspod中转换成标准promethesu配置文件供prometheusserver使用。各......
  • 从零开始:苹果手机免越狱群控系统的快速入门指南
    对于初次接触苹果手机免越狱群控系统的用户来说,可能会感到有些困惑和不知所措。本章将提供一个详细的快速入门指南,帮助你从零开始,轻松掌握如何设置和使用这项强大的技术。免越狱群控系统概述苹果手机免越狱群控系统是一种通过合法合规的技术手段,在不破坏iOS系统安全性的前提......
  • Jenkin window bat批处理脚本如何请求api,获取json对象返回值数据
    需求:在jenkins的windowsbat批处理脚本中,请求一个api返回json数据,然后获取返回值的某个指定key的value直接上脚本:cdC:\workspace\jenkinxx\workspace\job-ta-webgitcheckoutmastergitpulldir::使用curl请求APIcurl-shttp://xx.xx.xx.xx:8088/testapi/getData......
  • Jenkin window bat批处理脚本如何 获取json对象返回值数据
    前两天有这么个小需求:在cmd中运行某测试工具后/请求某个api后,会返回一个json结果,其中有一个参数的值每次都变且经常要用,正常情况复制粘贴就好了,但这个值非常长,配上cmd的标记+粘贴的行为,就很酸爽了。然后就想快速提取这个值,顺着cmd的这个思路,就走上了批处理的道路。借这个机会,简......
  • JavaScript 中的“造物”艺术:对象、构造函数与原型
    走进JS,走近讶语在JavaScript的世界里,对象和面向对象编程(OOP)是不可或缺的一部分。本文将带你深入了解JavaScript中对象的创建方式,特别是ES6中引入的class语法,以及传统的构造函数和原型的概念。希望通过这篇文章,你能对JavaScript的面向对象编程有一个更深入的理解。1.造对象......
  • Docker - Minio对象存储部署
    Minio是一个基于ApacheLicensev2.0开源协议的对象存储服务。非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。集群部署也是非常的简单,本篇文章只讨论单机部署,集群有需要后续跟进......