JVM对象创建与内存分配

对象的创建

对象创建的主要流程:

类加载推荐博客:JVM类加载机制详解

在这里插入图片描述

类加载检查

虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。

new指令对应到语言层面上讲是,new关键词、对象克隆、对象序列化等。

分配内存

在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需内存的大小在类加载完成后便可完全确定,为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。

这个步骤有两个问题:

  1. 如何划分内存。
  2. 在并发情况下, 可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况。
划分内存的方法:
  • 指针碰撞(Bump the Pointer)(默认用指针碰撞): 如果Java堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离。
  • 空闲列表(Free List): 如果Java堆中的内存并不是规整的,已使用的内存和空闲的内存相互交错,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例, 并更新列表上的记录。
解决并发问题的方法:
  • CAS(compare and swap): 虚拟机采用CAS配上失败重试的方式保证更新操作的原子性来对分配内存空间的动作进行同步处理。
  • 本地线程分配缓冲(Thread Local Allocation Buffer,TLAB): 把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存。通过-XX:+/-UseTLAB参数来设定虚拟机是否使用TLAB(JVM会默认开启-XX:+UseTLAB),-XX:TLABSize 指定TLAB大小。

初始化零值

内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头), 如果使用TLAB,这一工作过程也可以提前至TLAB分配时进行。这一步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

设置对象头

初始化零值之后,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头Object Header之中。

在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)实例数据(Instance Data)对齐填充(Padding)。 HotSpot虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。对象头的另外一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

对于大部分处理器,对象以8字节整数倍来对齐填充都是最高效的存取方式。

执行init()方法

执行init()方法,即对象按照程序员的意愿进行初始化。对应到语言层面上讲,就是为属性赋值(注意,这与上面的赋零值不同,这是由程序员赋的值),和执行构造方法。

对象的内存分配

在这里插入图片描述

对象栈上分配

我们通过JVM内存分配可以知道JAVA中的对象都是在堆上进行分配,当对象没有被引用的时候,需要依靠GC进行回收内存,如果对象数量较多的时候,会给GC带来较大压力,也间接影响了应用的性能。为了减少临时对象在堆内分配的数量,JVM通过逃逸分析确定该对象不会被外部访问。如果不会逃逸可以将该对象在栈上分配内存,这样该对象所占用的内存空间就可以随栈帧出栈而销毁,就减轻了垃圾回收的压力。

在这里插入图片描述

JIT热点探测技术

在 HotSpot 虚拟机中的热点探测是JIT优化的条件,热点探测是基于计数器的热点探测,采用这种方法的虚拟机会为每个方法建立计数器统计方法的执行次数,如果执行次数超过一定的阈值就认为它是“热点方法”

虚拟机为每个方法准备了两类计数器:方法调用计数器(Invocation Counter)回边计数器(Back Edge Counter)。在确定虚拟机运行参数的前提下,这两个计数器都有一个确定的阈值,当计数器超过阈值溢出了,就会触发 JIT 编译。

  • 方法调用计数器: 用于统计方法被调用的次数,方法调用计数器的默认阈值在客户端模式下是 1500 次,在服务端模式下是 10000 次(我们用的都是服务端,java –version查询),可通过-XX: CompileThreshold来设定。
  • 回边计数器: 用于统计一个方法中循环体代码执行的次数,在字节码中遇到控制流向后跳转的指令称为“回边”(Back Edge),该值用于计算是否触发 C1 编译的阈值,在不开启分层编译的情况下,在服务端模式下是10700

满足以上两个条件之一,都是热点方法。

逃逸分析的原理

分析对象动态作用域,当一个对象在方法中定义后,它可能被外部方法所引用。比如:调用参数传递到其他方法中,这种称之为方法逃逸。甚至还有可能被外部线程访问到,例如:赋值给其他线程中访问的变量,这个称之为线程逃逸。从不逃逸到方法逃逸到线程逃逸,称之为对象由低到高的不同逃逸程度。

如果确定一个对象不会逃逸出线程之外,那么让对象在栈上分配内存可以提高JVM的效率。

当然逃逸分析技术属于JIT的优化技术,所以必须要符合热点代码,JIT才会优化,另外对象如果要分配到栈上,需要将对象拆分,这种编译优化就叫做标量替换技术。

public User test1() {
   User user = new User();
   user.setId(1);
   user.setName("test");
   //TODO 保存到数据库
   return user;
}

public void test2() {
   User user = new User();
   user.setId(1);
   user.setName("test");
   //TODO 保存到数据库
}

很显然test1方法中的user对象被返回了,这个对象的作用域范围不确定。test2方法中的user对象我们可以确定当方法结束这个对象就可以认为是无效对象了,对于这样的对象我们其实可以将其分配在栈内存里,让其在方法结束时跟随栈内存一起被回收掉。

标量替换

通过逃逸分析确定该对象不会被外部访问,并且对象可以被进一步分解时,JVM不会创建该对象,而是将该对象成员变量分解若干个被这个方法使用的成员变量所代替,这些代替的成员变量在栈帧或寄存器上分配空间,这样就不会因为没有一大块连续空间导致对象内存不够分配。开启标量替换参数(-XX:+EliminateAllocations),JDK7之后默认开启。

标量与聚合量: 标量即不可被进一步分解的量,而JAVA的基本数据类型就是标量(如:int,long等基本数据类型以及reference类型等),标量的对立就是可以被进一步分解的量,而这种量称之为聚合量。而在JAVA中对象就是可以被进一步分解的聚合量。

对象在Eden区分配

大多数情况下,对象在新生代中 Eden 区分配。当 Eden 区没有足够空间进行分配时,虚拟机将发起一次Minor GC。

大量的对象被分配在eden区,eden区满了后会触发minor gc,可能会有99%以上的对象成为垃圾被回收掉,剩余存活的对象会被挪到为空的那块survivor区,下一次eden区满了后又会触发minor gc,把eden区和survivor区垃圾对象回收,把剩余存活的对象一次性挪动到另外一块为空的survivor区,因为新生代的对象都是朝生夕死的,存活时间很短,所以JVM默认的8:1:1的比例是很合适的,让eden区尽量的大,survivor区够用即可。

大对象直接进入老年代

大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。JVM参数 -XX:PretenureSizeThreshold 可以设置大对象的大小,如果对象超过设置大小会直接进入老年代,不会进入年轻代,这个参数只在SerialParNew两个收集器下有效。

长期存活的对象将进入老年代

既然虚拟机采用了分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应放在新生代,哪些对象应放在老年代中。为了做到这一点,虚拟机给每个对象一个对象年龄(Age)计数器。

如果对象在 Eden 出生并经过第一次Minor GC后仍然能够存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并将对象年龄设为1。对象在Survivor中每熬过一次MinorGC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁,CMS收集器默认6岁,不同的垃圾收集器会略微有点不同),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。

对象动态年龄判断

当前放对象的Survivor区域里(其中一块区域,放对象的那块s区),一批对象的总大小大于这块Survivor区域内存大小的50%(-XX:TargetSurvivorRatio可以指定),那么此时大于等于这批对象年龄最大值的对象,就可以直接进入老年代了,例如Survivor区域里现在有一批对象,年龄1+年龄2+年龄n的多个年龄对象总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代。这个规则其实是希望那些可能是长期存活的对象,尽早进入老年代。对象动态年龄判断机制一般是在minor gc之后触发的。

对象内存回收

堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断哪些对象已经死亡(即不能再被任何途径使用的对象)。

通过一下两种算法判断是否是垃圾对象。

引用计数法

假设有个一对象ClassA,任何一个对象引用了ClassA,ClassA的引用计数器就会加1,引用失效引用计数器就会减1。如果ClassA引用计数器为0,那么ClassA就会被回收。

这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间相互循环引用的问题。

可达性分析算法

可达性分析法也被称之为根搜索法,可达性是指,如果一个对象被一个或多个在程序中的变量,通过直接或间接方式被其它可达的对象引用,那么该对象就是可达的。更准确的说,一个对象只有满足下述两个条件之一,就会被判断为可达的:

  1. 对象是属于根集中的对象
  2. 对象被一个可达的对象引用

在这里,我们引出了一个专有名词,“根集”,指正在执行的Java程序可以访问的引用变量的集合,程序可以使用引用变量访问对象的属性和调用对象的方法。在 JVM 中,会将以下对象标记为根集中的对象,具体包括:

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象
  2. 方法区中的常量引用的对象
  3. 方法区中的类静态属性引用的对象
  4. 本地方法栈中 JNI(Native 方法)的引用对象

根集中的对象称之为GC Roots,即根对象。可达性分析法的基本思路是:将一系列的根对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,如果一个对象到根对象没有任何引用链相连,那么这个对象就不是可达的,也称之为不可达对象。

在这里插入图片描述
如上图所示,形象的展示了可达对象与不可达对象,其中灰色的是不可达对象,可以被垃圾收集的对象。在可达性分析法中,对象有两种状态,要么是可达的、要么是不可达的,在判断一个对象的可达性的时候,就需要对对象进行标记。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

海外IP代理科普——API代理是什么?怎么用?

随着互联网的不断发展,越来越多的企业开始使用API(应用程序接口)来实现数据的共享和交流。而在API使用中,海外代理IP也逐渐普及。那么,什么是API代理IP呢?它有什么作用?API接口有何用处&#xf…

Tomcat 9.0.54源码环境搭建

一. 问什么要学习tomcat tomcat是目前非常流行的web容器,其性能和稳定性也是非常出色的,学习其框架设计和底层的实现,不管是使用、性能调优,还是应用框架设计方面,肯定会有很大的帮助 二. 运行源码 1.下载源…

数据类型扩展02

1、字符串拓展 所有的字符本质还是数字。 char c1 a;char c2 中;System.out.println("c1:"c1);System.out.println("c1转换:"(int)c1);System.out.println("c2:"c2);System.out.println("c2转换:"(int)c2); 执行结果 c1:a c1转换:…

Linux:动静态库

目录 一、软硬链接 1、软链接 2、硬链接 二、动态库和静态库 编写一个库 ①静态库 使用静态库的方法 ②动态库 使用动态库的方法 库存在的意义 一、软硬链接 软硬链接的本质区别就是:有无独立的inode 软链接有独立的inode,也就意味着软链接是一…

redis高可用---持久化

redis高可用 在集群当中有一个非常重要的指标,提供正常服务的时间的百分比(365天) 99.9%,redis高可用含义更广泛,支持服务是指标之一,数据容量扩展,数局的安全性。(容量、安全性) redis中实现高…

【HCSD大咖直播】亲授大厂面试秘诀【云驻共创】

同学们,毕业季是否找到了自己心仪的工作呢?是否了解大厂面试流程、要求以及技巧呢?华为云IoT高级工程师,传授大厂面试秘诀,教大家如何轻松get大厂offer!提前为大厂面试做准备,赢在起跑线&#x…

新安装win11,搜索框无法输入的问题

正确的做法是如下: 1首先进入win11系统,在搜索框中输入“ 控制面板 ”将其打开2在控制面板中找到“时间和语言“ 标题 再选择“ 语言和区域”, 标题 在显示的语言上面,点击省略号,进入语言选项 标题 在键盘处,删除不需要的输入法…

C++纯虚函数和抽象类 制作饮品案例(涉及知识点:继承,多态,实例化继承抽象类的子类,多文件实现项目)

一.纯虚函数的由来 在多态中&#xff0c;通常父类中虚函数的实现是毫无意义的&#xff0c;主要都是调用子类重写的内容。例如&#xff1a; #include<iostream>using namespace std;class AbstractCalculator { public:int m_Num1;int m_Num2;virtual int getResult(){r…

docker和docker-compose生产的容器,不在同一个网段,解决方式

在实际项目中&#xff0c;使用docker run xxXx 和docker-compose up -d 不在同一个网段&#xff0c;一个是默认是172.17.x.x, 另一个是172.19.x.x。为解决这个问题需要自定义一个网络&#xff0c;我命名为“my-bridge” 首先熟悉几条命令&#xff1a; docker network ls 或…

如何理解低代码开发工具?

目录 一、低代码平台是什么 1. 数据输入 2. 数据流转 3. 数据加工 4. 数据输出 &#xff08;1&#xff09;视图&#xff1a;单表的数据的简单展示 &#xff08;2&#xff09;看板&#xff1a;提供单表、多表数据的多种图表组合展示 二、低代码能力 ➪ 全栈可视化编程&#xff1…

你知道Linux操作系统的前世今生吗?Linux系统又该如何搭建呢?

文章目录 前言1. Linux 是什么1.1 Unix & Linux 发展历程图1.2 Linux 的发展1.3 Linux 的发行版 2. Linux 环境搭建2.1 环境搭建方式2.2 使用云服务器 3. 使用终端软件连接到 Linux3.1 什么是终端软件3.2 下载安装 XShell3.3 使用 XShell 登陆主机 总结 前言 可能很多人都…

labelImg

labelImg 在anaconda虚拟环境中安装labelImg 进入conda虚拟环境DL2中 输入命令&#xff1a; pip install PyQt5 pip install pyqt5-tools pip install lxml pip install labelimg PyQt5:是用于创建GUI应用程序的跨平台工具包&#xff0c;它将Python与Qt库融为一体 Lxml&#…

深信服技术认证“SCSA-S”划重点:渗透测试工具使用

为帮助大家更加系统化的学习网络安全知识&#xff0c;尽快通过深信服安全服务认证工程师认证&#xff0c;深信服推出“SCSA-S认证备考秘笈”共十期内容&#xff0c;“考试重点”内容框架&#xff0c;帮助大家快速get重点知识~ 划重点来啦 深信服安全服务认证工程师&#xff08;…

CVE-2023-0179提权利用

前言 在CVE-2023-0179-Nftables整型溢出中&#xff0c;分析了漏洞的成因&#xff0c;接下来分析漏洞的利用。 漏洞利用 根据漏洞成因可以知道&#xff0c;payload_eval_copy_vlan函数存在整型溢出&#xff0c;导致我们将vlan头部结构拷贝到寄存器&#xff08;NFT_REG32_00-N…

【SpringBoot】通过profiles设置环境

效果图&#xff0c;通过profiles设置环境 在父级pom.xml中添加配置 <profiles><profile><id>dev</id><properties><application.environment>dev</application.environment></properties><activation><activeByDefau…

OceanBase:OBServer节点管理

目录 1.查看节点 2.添加节点 2.1 创建数据目录 2.2.OceanBase 运行时所依赖的部分三方动态库 2.3.安装 OceanBase 数据库的 RPM 包 2.4.启动节点 observer 进程 2.5.向集群中添加节点 3.隔离节点 4.重启节点 4.1 停止服务 4.2 转储 4.3 关闭进程 4.4 启动进程 4.…

【原创】为MybatisPlus增加一个逻辑删除插件,让XML中的SQL也能自动增加逻辑删除功能

前言 看到这个标题有人就要说了&#xff0c;D哥啊&#xff0c;MybatisPlus不是本来就有逻辑删除的配置吗&#xff0c;比如TableLogic注解&#xff0c;配置文件里也能添加如下配置设置逻辑删除。 mybatis-plus:mapper-locations: classpath*:mapper/*.xmlconfiguration:mapUnd…

Java-认识String类

本章重点&#xff1a; 1. 认识 String 类 2. 了解 String 类的基本用法 3. 熟练掌握 String 类的常见操作 4. 认识字符串常量池 5. 认识 StringBuffer 和 StringBuilder 1.String类的重要性 在C语言中已经涉及到字符串了&#xff0c;但是在C语言中要表示字符串只能使用字符数组…

C++中的内存管理

✨前言✨ &#x1f4d8; 博客主页&#xff1a;to Keep博客主页 &#x1f646;欢迎关注&#xff0c;&#x1f44d;点赞&#xff0c;&#x1f4dd;留言评论 ⏳首发时间&#xff1a;2023年11月21日 &#x1f4e8; 博主码云地址&#xff1a;博主码云地址 &#x1f4d5;参考书籍&…

如何选择适合的开源框架来构建微服务架构?

随着科技的飞速发展&#xff0c;云计算和大规模应用的需求日益显著&#xff0c;这促使微服务架构在软件开发领域中占据了主流地位。微服务架构的广泛应用为开发人员提供了灵活性、可伸缩性和高可用性&#xff0c;从而推动了快速的应用程序开发。然而&#xff0c;在构建微服务架…