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

一、对象创建

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 标记-整理算法

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

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

会变化。

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

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

相关文章

Vulnhub靶机之reven 1

一、信息收集 nmap扫描网段,靶机地址为192.168.145.129。 nmap -sP 192.168.145.* 扫一下端口,开放了22、80、111、50967。 nmap -sT -T4 -p1-65535 192.168.145.129 再看一下目录情况,发现一个疑似后台登录的地址。 dirsearch -u http://…

基于Python + Flask+ Mysq实现简易留言板

使用Python Flask Mysql实现简易留言板,包括网友编辑留言、修改留言,删除留言、分页显示四大功能。 写出留言板建设过程,包括开发使用工具、留言板模块设计、数据库设计、页面设计、关键技术。 留言板建设过程总结 一.开发使用…

图像处理ASIC设计方法 笔记28 无效像元检测

(一)无效像元检测 P149 基于场景的红外焦平面无效像元检测 空间噪声的两个来源1 各探测源相应的非均匀性产生 2 IRFPA组件制造过程中产生 先进的工艺水平可以做到成品期间不存在连续分布或中心分布的无效像元,无效像元率在0.5%以内。国内…

LeetCode | 1.两数之和

这道题,很容易想到的是暴力解,直接一个双重循环,对于数组中的每一个数,都去遍历其他数字,看能不能找到数字等于target-nums[i]的数字,时间复杂度为 O ( n 2 ) O(n^2) O(n2) 但是通过其他题目,我…

动态规划(多重背包+完全背包)

P2851 [USACO06DEC] 最少的硬币 G 题解:从题目上看到那个有n种不同的货币,对于买家来说每个货币有C[ i ]个,是有限个数的,但是对于卖家来说 每个货币都是无限的,题目中要我们求的是买到这个物品的最小交易的货币数&…

transformer - 注意力机制

Transformer 的注意力机制 Transformer 是一种用于自然语言处理任务的模型架构,依赖于注意力机制来实现高效的序列建模。注意力机制允许模型在处理一个位置的表示时,考虑输入序列中所有其他位置的信息,而不仅仅是前面的几个位置。这种机制能…

3.大模型高效微调PEFT

大模型高效微调(PEFT)技术 预训练模型的背景 预训练与微调:传统的微调方法通常涉及对整个预训练模型的参数进行再训练,以适应特定任务。这虽然有效,但计算成本高,且需要大量的标记数据。模型结构:像BERT或GPT这样的模型通常包含数亿甚至数十亿个参数,构成一个深层次的…

JWT 从入门到精通

什么是 JWT JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案 JSON Web Token Introduction - jwt.ioLearn about JSON Web Tokens, what are they, how they work, when and why you should use them.https://jwt.io/introduction 一、常见会…

转型AI产品经理(6):“ 序列位置效应”如何应用在Chatbot产品中

序列位置效应是心理学中的一个记忆现象,指的是人们对一系列信息的记忆效果受到信息在序列中位置的影响。具体来说,人们通常更容易记住列表的开头和结尾部分的项目,而对中间部分的项目记忆较差。这个效应可以进一步分为“首因效应”和“近因效…

递归

特点:有一个初始状态;后续的情况可由前面的状态推出。(斐波拉契,求阶乘) 一 .写出分段函数(有初值,递推关系)可以递归 用if-else连接

Day47 代码随想录打卡|二叉树篇---最大二叉树

题目(leecode T654): 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点,其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前缀上 构建左子树。递归地在最大值 右边 的 …

java版多语言抢单系统 多语言海外AEON抢单可连单加额外单源码 抢单平台搭建开发 抢单开挂的软件

此套是全新开发的java版多语言抢单系统。 后端java,用的若依框架,这套代码前后端是编译后的,测试可以正常使用,语言繁体,英文,日语 源码大小:155M 源码下载:https://download.csd…

初识 java 2

1. idea 的调试 1. 点击鼠标左键设置断点 2.运行到断点处 点击 或点击鼠标右键,再点击 使代码运行到断点处,得到 2. 输出到控制台 System.out.println(value);//输出指定的内容,并换行 value 要打印的内容System.out.print(value);…

《精通ChatGPT:从入门到大师的Prompt指南》附录A:常用Prompt示例

附录A:常用Prompt示例 在《精通ChatGPT:从入门到大师的Prompt指南》的附录A中,我们将展示一系列常用的Prompt示例,帮助读者更好地理解和应用Prompt技术。每个示例将包含Prompt的描述、使用场景、预期结果以及实际输出。希望这些示…

Leetcode3040. 相同分数的最大操作数目 II

Every day a Leetcode 题目来源:3040. 相同分数的最大操作数目 II 解法1:记忆化搜索 第一步可以做什么?做完后,剩下要解决的问题是什么? 删除前两个数,剩下 nums[2] 到 nums[n−1],这是一个…

Nvidia的成功与竞争:CEO黄仁勋的自信与挑战

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

【微信小程序】事件分类以及阻止事件冒泡

在微信小程序中,事件分为冒泡事件和非冒泡事件两大类,它们的区别在于事件是否能从原始触发组件开始,向父级组件传播(即“冒泡”)。 冒泡事件:当一个组件上的事件被触发后,不仅当前组件会接收到这…

Pytorch学习11_神经网络-卷积层

1.创建神经网络实例 import torch import torchvision from torch import nn from torch.nn import Conv2d from torch.utils.data import DataLoaderdatasettorchvision.datasets.CIFAR10("../dataset_cov2d",trainFalse,transformtorchvision.transforms.ToTensor(…

软件项目安全保证措施(Word原件)

软件安全保证措施 一、身份鉴别 二、访问控制 三、通信完整性、保密性 四 、数据完整性 六、数据保密性 七、应用安全支撑系统设计获取本原件及更多资料:本文末个人名片。

vue如何使用slot

1. vue2 如何使用slot 1.1. 默认插槽(Default Slot)1.2. 具名插槽(Named Slot)1.3. 作用域插槽(Scoped Slot) 2. vue3 如何使用slot 2.1. 默认插槽(Default Slot)2.2. 具名插槽&…