JVM对象分配和垃圾回收机制

news2025/1/11 7:04:02

一、对象创建

1.1 符号引用

new 创建一个对象,需要在JVM创建对象。

符号引用:目标对象采用一个符号表示,类A加载的时候,如果成员变量类B还没有被加载进来,采用一个符号(字面量)来表示,这种引用就称为符号引用。

直接引用:真实地址。

检查加载的时候,检查类B是否加载,已加载的话,将符号引用修改为直接引用。

1.2 JVM创建对象过程

1)检查加载,new指令创建对象,首先需要检查对象对应的Class类是否已经加载。未加载需要先加载类。

2)分配内存:在堆空间划出一块确定的内存,分配给对象。因为JAVA支持多线程,分配对象的需要考虑并发安全性。

3)内存空间初始化:对象创建以后,成员初始值赋“零”值。

4)设置:

5)对象初始化

1.3 划分内存的方式

划分内存有两种方式:指针碰撞和空闲列表。

指针碰撞:如果Java堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离,这种分配方式称为"指针碰撞"。

指针碰撞适用于Serial和ParNew等不会产生内存碎片的垃圾收集器。
新生代通常使用指针碰撞进行内存分配。

空闲列表:如果Java堆中的内存并不是规整的,已使用的内存和空闲的内存相互交错,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为"空闲列表"。

1.3.1 比较

指针碰撞效率比较高,采用指针碰撞的决定性因素:堆空间是否规整。

1.4 线程并发对象创建安全

Java支持多线程并发,因此划分内存分配对象的时候,一定存在并发安全问题。

CAS

线程分配内存的时候,首先查询空闲位置,然后通过CAS操作,采用CAS操作向空闲内存位置申请 对应对象大小的空间,如果申请成功,则分配成功;如果申请失败,则说明此空闲位置已经被别的线程占据,此位置已经不是空闲位置。继续上面的操作,查找下一个可用的空闲位置进行分配。

CAS是一种无锁机制,CPU指令,原子指令。 

TLAB

指本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)

TLAB的目的是在为新对象分配内存空间时,让每个Java应用线程能使用自己专属的分配指针来分配空间,减少同步开销。

TLAB只是让每个线程拥有私有的分配指针,但底下存对象的内存空间还是给所有线程访问的,只是其它线程无法在这个区域分配而已。当一个TLAB用满(分配指针top撞上分配极限end了),就新申请一个TLAB。空间换时间

开启参数 -XX+UseTLAB

1.5 对象的内存空间分布

对象在内存中的分配,包括 对象头(MarkWork 8字节+ 类型指针4字节),实例数据(对象成员)+对齐位。

1.5.1 对齐填充

对象要求是8个字节的整数倍, 当对象分配空间不足8的倍数时,自动填充补齐。

1.5.2 数组对象的空间分布

 数组对象:对象头(MarkWord 8字节+类型指针4字节)+数组长度(4字节)+对齐位。

数组对象与普通对象相比,多了一个数组长度的字段,标记数组长度。

1.6 对象的访问定位

1.6.1 使用句柄访问对象

引用存储的是一个地址,该地址是句柄的地址,而句柄是一种结构,分别存储 实例指针和类型指针 这两种指针,(实例指针是指向堆中的对象实例,而类型指针指向的是在方法区中该对象所属类型)。当要访问对象时,先通过引用访问句柄,再通过句柄访问对象实例以及对象类型信息。句柄是存储在堆中的,如果使用这种方式,那么就会从堆中分出一块内存用作句柄池

1.6.2 使用直接指针访问对象

引用存储的是对象实例在堆中的地址,通过引用可以直接访问对象实例。

比较

1)直接指针访问对象的优点:效率高,一次就可以找到对象。缺点是垃圾收集器移动对象时需要修改引用,因为垃圾回收涉及对象移动,对象的实际地址会有变化。

2)句柄访问模式的优点:对象经过多次移动时,虚拟机只需要修改句柄中的指向对象实例的指针即可,不用修改引用。垃圾回收时效率高。缺点是需要额外维护对象池,访问对象效率低,需要两个步骤才能得到对象实例。

目前虚拟机主要使用的直接指针访问的方式。因为访问对象的频率要远高于垃圾回收的频率。

1.7 对象存活判断

在JVM中,对象是自动化的回收机制,用户只需要管对象的创建,不要手动去释放对象,JVM垃圾收集器负责判断哪些对象是垃圾,并且对垃圾对象的回收。

1.7.1 垃圾对象判断算法

1.7.1.1 引用计数法

引用计数算法是通过判断对象的引用数量来决定对象是否可以被回收。引用数为0时,说明对象没有被其他对象引用,可回收。

实现方式:

给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是可被回收的对象。

优点:简单、高效

缺点:很难处理循环引用,相互引用的两个对象则无法释放。因此目前主流的Java虚拟机都摒弃掉了这种算法。

python使用的引用计数法。对于循环引用怎么解决?通过开启一个额外线程检测是否存在循环应用,村换引用的对象特殊处理。

1.7.1.2 可达性分析算法

又叫根可达算法,从GC Roots(每种具体实现对GC Roots有不同的定义)作为起点,向下搜索它们引用的对象,可以生成一棵引用树,树的节点视为可达对象,反之视为不可达。引用树上的节点,都是可达的,需要存活。

1.7.1.2.1 GC Roots

GC Roots包括四类:静态变量,虚拟机栈变量,常量池,JNI指针(JNI创建的对象)

这种情况下,即使存在循环引用,但是不在引用树中,可以回收。 

除了这四种GC Roots,还存在其他的吗?

内部引用:Class对象、异常对象、类加载器

内部锁:synchronized对象

内部对象:JMXBean

临时对象:跨代引用

1.8 Class对象回收的条件

Class对象回收的条件比较苛刻。满足所有的条件:

1)Class new出的对象都要被回收掉;

2)对应的类加载也要被回收;

3)没有通过反射使用Class类

4)没有 up-level 依赖的类(即正在加载的类引用了正在初始化的超类或接口)。没有被其他Class类引用。

5)参数控制允许回收Class对象。

-Xnoclassgc: 应用类GC

当您在启动时指定-Xnoclassgc时,应用程序中的类对象在GC期间将保持不变,并且将始终被视为活动的。这可能会导致更多的内存被永久占用,如果不小心使用,会引发内存不足异常。

1.9 对象的自我拯救Finalize

1.10 各种引用

强引用:存在引用时,不会被会受到,即使内存不足时,会抛出OutOfMemary

软引用:内存不足时可以被回收掉

弱引用:只要执行GC就可以被回收掉

虚引用:随时被回收掉。

https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

二、对象分配原则

几乎所有的对象都在堆中分配。 

2.1 栈上对象分配

两个条件:热点代码+逃逸分析

2.1.1 逃逸分析

对象的存活声明周期能不能逃出这个方法。

分配在栈中的对象,不能被其他线程共享。

开启开关: -XX:+DoEscapeAnalysis 

启用逃逸分析。默认是开启的。只有Java HotSpot Server VM支持这个选项。

2.2 大对象直接进入老年代

只有Serial和ParNew这两款垃圾收集器才生效。

启动条件:-XX:PretenureSizeThreshold=4m,

new的对象超过4M直接进入老年代。

2.3 长期存活的队形进入老年代

对象在新生代创建以后,结果一次垃圾收集,仍然存活的对象,age=age+1,年龄增长1岁。当年龄达到15时,此对象需要进阶老年代。

允许设置最大年龄,不能超过15岁。

-XX:MaxTenuringThreshold=threshold

2.4 动态年龄判断

最大年龄太小的话,导致对象不能尽可能的回收,快速进入老年代,导致老年代臃肿;

年龄太大的话,长期存活的对象反复进行回收检测,浪费效率;

当前存放对象的Surnvivor区域里(其中一块区域,存放对象的那块s区),一批对象的总大小大于这块Sunvivor区域内存大小的50%(由-XX:TargetSurvivorRatio参数指定),那么此时大于等于这批对象年龄最大值的对象,就可以直接进入老年代了。

这个规则其实是希望那些可能是长期存活的对象,尽早进入老年代。
对象动态年龄判断机制一般是在minor gc之后触发的。

2.5 空间分配担保

为什么要设置老年代空间分配担保机制?

  • 空间担保分配是指在发生Minor GC之前
  • 只要老年代的连续空间 大于 新生代对象总大小或者历次晋升的平均大小 就会进行Minor GC,否则将进行Full GC。

内存分配是在JVM在内存分配的时候,新生代内存不足时,把新生代的存活的对象搬到老生代,然后新生代腾出来的空间用于为分配给最新的对象。这里老生代是担保人。在不同的GC机制下,也就是不同垃圾回收器组合下,担保机制也略有不同。在Serial+Serial Old的情况下,发现放不下就直接启动担保机制;在Parallel Scavenge+Serial Old的情况下,却是先要去判断一下要分配的内存是不是**>=Eden区大小的一半**,如果是那么直接把该对象放入老生代,否则才会启动担保机制。

 2.6 分代垃圾收

2.6.1 什么是垃圾回收?

JAVA特有功能,垃圾收集意味着程序不再需要的对象是无用信息,这些信息将被丢弃回收。

2.6.2 分代垃圾回收

MinorGC/YoungGC: 新生代垃圾回收

MajorGC: 老年代垃圾回收,只有CMS

FullFC: 堆区域全部垃圾回收(新生代和老年代)

2.7 垃圾回收算法 

垃圾回收有三种主要算法:复制算法,

2.7.1 复制算法

原理:把内存空间一份为二,一半用于保存对象,一半用于预留空间。当进行垃圾回收时,将所有存活对象从工作空间复制到预留空间。复制完成后,对工作空间整体清理。

新生代绝大部分对象都是朝生夕死。

 

优化复制算法,划分Eden区。Eden区:S1:S2=8:1:1,空间利用率能够达到90%。

加强版的复制算法。

2.7.2 标记清除算法

原理:采用可达性分析算法,标记存货对象,清理掉垃圾对象。这种清理算法会产生不连续的空闲内存区域。

2.7.3 标记-整理算法

原理:采用可达性分析算法标记存活对象,对存活对象进行整理,然后清理垃圾对象。 

对象移动后,哈希值变化吗?

会变化。

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

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

相关文章

Linux C语言:指针的运算

一、指针的算术运算 1、指针运算 指针运算是以指针所存放的地址作为运算量而进行的指针运算的实质就是地址的计算 2、指针的算数运算 指针加上整数,指针减去整数, 指针递增,指针递减和两个指针相减。 指针加减一个n的运算: px n px - n 移动步长…

LeetCode | 2879.显示前三行

在 pandas 中,可以使用 head() 方法来读取 DataFrame 的前几行数据。如果想读取指定数量的行,可以在 head() 方法中传入一个参数 n,读取前 n 行 import pandas as pddef selectFirstRows(employees: pd.DataFrame) -> pd.DataFrame:retur…

mybatisplus(原理)使用方法引用的形式获取实体类对应数据库的列名

我们现在正常来看 一个mybatisplus正常的查询语句 我们可以看到 ,再如上的代码中 我们使用了 Address::getuserId 方法引用,但是我们把方法引用改成lambda表达式的形式的时候不会报错,但是运行的时候报错。为什么…

Tkinter 组件详解之Entry

Tkinter 组件详解之Entry Entry(输入框)组件通常用于获取用户的输入文本。 何时使用 Entry 组件? Entry 组件仅允许用于输入一行文本,如果用于输入的字符串长度比该组件可显示空间更长,那内容将被滚动。这意味着该字符串将不能被全部看到(你可以用鼠标或键盘的方向键调…

[2024-06]-[大模型]-[DEBUG]- ollama webui 11434 connection refused

报错:host.docker.internal:11434 ssl:default [Connection refused] 将/etc/systemd/system/ollama.service中加上如下红框两行 Environment"OLLAMA_HOST0.0.0.0" Environment"OLLAMA_ORIGINS*"然后 systemctl daemon-reload systemctl rest…

速卖通测评攻略:轻松提升店铺曝光度和吸引力

在速卖通平台上,产品排名,店铺曝光的提升无疑是所有卖家追求的目标,因为这直接关联着产品曝光量的增加和潜在销售机会的扩大。然而,提升产品排名并非一蹴而就,它需要一系列的策略和技巧。那么,接下来就让我…

【网络安全】跨站脚本攻击漏洞—HTML前端基础

目录 一、HTML概述 1.1 head部分 1.2 body部分 1.3 HTML特殊符号 二、JavaScript概述 2.1 HTML中JavaScript的存在方式 2.2 DOM操作 2.3 BOM操作 跨站脚本攻击(Cross-site scripting,通常缩写为XSS)是一种常见的网络安全漏洞&#xff…

区块链实验室(36) - 交叉编译Ethereum的客户端全套工具

停滞了一段时间,重新回到区块链实验。前面在“区块链实验室(31) - 交叉编译Ethereum的客户端Geth”中仅编译出客户端工具geth。编译Ethereum全套工具的代码如下。 #!/bin/bash ## abigen CGO_ENABLED0 GOOSlinux GOARCHarm64 /usr/local/go/bin/go build -ldflags …

成都跃享未来教育抖音小店深度解析靠谱与否

在如今网络购物日益繁荣的时代,抖音小店以其独特的平台优势和庞大的用户基础,吸引了越来越多的商家入驻。成都跃享未来教育咨询有限公司便是其中之一,它的抖音小店究竟靠不靠谱呢?今天,我们就来一起揭开这个谜底。 首…

现货黄金交易多少克一手?国内外情况大不同

如果大家想参与国际市场上的现货黄金交易,就应该从它交易细则的入手,先彻底认识这个品种,因为它是来自欧美市场的投资方式,所以无论是从合约的计的单位,计价的货币,交易的具体时间,以及买卖过程…

乡村振兴的多元化产业发展:推动农村一二三产业融合发展,培育乡村新业态,打造多元化发展的美丽乡村

一、引言 乡村振兴是我国当前及未来一段时间内的重大战略任务,旨在促进农村经济的全面发展,提高农民的生活水平,实现城乡融合发展。在乡村振兴的进程中,推动农村一二三产业融合发展,培育乡村新业态,是打造…

linux安装anconda后,之前的python环境如何加载到anconda环境中

一、问题描述 由于某种原因,我们需要在系统中安装多个环境,我们自然想到安装anconda来解决这个问题。但是当我们安装好anconda后,发现我们未安装anconda之前的python环境使用不了了。那么我们如何将之前的python环境放到conda 环境中呢。 二…

7种常用数据分析方法,建议收藏学习(下)

在上一篇内容里,我们提到了常用的数据分析放法,比如:漏斗分析法、留存分析法、分组分析法、矩阵分析法。没有看到的上篇推送的小伙伴可以回过头去查看我们之前的推送。 今天,我们继续讲后面的内容:关联分析法、指标分…

SpringBoot快速部署(2)—不使用docker的常规方法

一、软件下载和部署前准备 安装软件 获取软件安装包 然后上传到服务器的 /tmp 目录下。 软件:nginx、jdk、mysql 下载 X-shell 和 Xftp 注意:这个页面下载安装的才可以免费使用。家庭/学校免费 - NetSarang Website 安装jdk 1.8 tar -zxvf /tmp/jdk-…

程序猿大战Python——容器——字符串

字符串介绍 什么是Python容器 目标:了解Python容器是什么? 在现实生活中,我们知道容器是用来存放东西的,比如实验室里的烧杯等。 类似的,在Python中的容器是用来存放数据的。 与此同时,为了操作方便&…

springboot与flowable(3):启动、审批、各个Service服务

一、启动流程 流程定义与实例的关系类似于Java的类与对象,通过定义的id创建流程实例,编写测试代码: package org.example.flowabledemo2;import org.flowable.engine.RuntimeService; import org.flowable.engine.runtime.ProcessInst…

[论文阅读] (33)NDSS2024 Summer系统安全和恶意代码分析方向相关论文汇总

《娜璋带你读论文》系列主要是督促自己阅读优秀论文及听取学术讲座,并分享给大家,希望您喜欢。由于作者的英文水平和学术能力不高,需要不断提升,所以还请大家批评指正,非常欢迎大家给我留言评论,学术路上期…

盲盒小程序支付流程优化与风险防控策略

在盲盒小程序中,支付流程的优化和风险防控是提升用户体验和确保交易安全的关键环节。下面我们将深入探讨这两个方面的问题和应对策略。 一、支付流程优化 在支付流程优化方面,我们主要关注以下几点: 简化操作流程:通过减少不必…

男士内裤什么品牌质量好?盘点口碑最好的五款男士内裤

面对市场上琳琅满目的款式与品牌,如何挑选出最适合自己的那一款男士内裤一直是大家最大的疑问!今天,我们为您带来实用的选购技巧,并推荐五款备受好评的男士内裤,信儿帮助大家避免选到一些质量不好的男士内裤&#xff0…

Mysql 查询数据量

Mysql 查询数据量 Mysql 查询数据库数据量Mysql查询指定数据表数据量 Mysql 查询数据库数据量 查询命令如下 SELECTTABLE_NAME AS 表名,TABLE_ROWS AS 记录数,TRUNCATE (data_length / 1024 / 1024, 2) AS 数据容量(MB),TRUNCATE (index_length / 1024 / 1024, 2) AS 索引容量…