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

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

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

会变化。

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

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

相关文章

解密有道翻译响应数据末尾出现乱码问题的解决方法

运行解密响应数据程序: D:\Python\Python311\python.exe E:\baichuan\youdaos.py {"code":0,"dictResult":{"ce":{"word":{"trs"D:\Python\Python311\python.exe E:\baichuan\youdaospdm.pyD:\Python\Python31…

Linux 性能优化基础

文章目录 常见指标分类(USE法)常见性能工具CPU性能工具内存性能工具文件系统和磁盘I/O性能工具网络性能工具 根据指标找工具CPU性能内存性能文件系统和磁盘I/O网络性能 根据工具找指标CPU性能内存性能文件系统和磁盘I/O网络性能 CPU性能分析一般步骤内存…

GUI编程03-事件监听

事件监听是指当某个事件发生的时候干一些什么。 例如之前在关闭frame窗口时就写过一个window窗口监听,当点击左上角❌时调用System.exit进行程序关闭。 1.按钮监听 下面的例子是监听按钮Button被点击时触发的事件 同时我们将窗口关闭监听事件进行了优化&#xff…

教你一段代码激活计算机系统

方法简单粗暴,再也不用遭受未激活的烦恼了! 新建文本 输入代码,把文件后缀.txt改.bat slmgr /skms kms.03k.org slmgr /ato

如何用Vue3构建一个交互式音乐播放器

本文由ScriptEcho平台提供技术支持 项目地址:传送门 Vue.js 开发音乐播放器卡片 应用场景 这款音乐播放器卡片旨在为音乐应用程序提供一个现代而交互式的用户界面。它包含诸如歌曲信息、播放进度条和控制按钮等关键功能。 基本功能 **歌曲信息显示&#xff1a…

单细胞RNA测序(scRNA-seq) 理解Seurat对象存储信息含义和基本操作

单细胞测序技术是在单个细胞水平上,对基因组、转录组和表观基因组水平进行分析测序技术。bulk RNA-seq获得的是组织或器官等大量细胞中表达信号的均值,无法获取细胞之间的差异信息(即丢失了细胞的异质性), 而单细胞测序…

【文献阅读】一种多波束阵列重构导航抗干扰算法

引言 针对导航信号在近地表的信号十分微弱、抗干扰能力差的问题,文章提出了自适应波束形成技术。 自适应波束形成技术可以分为调零抗干扰算法和多波束抗干扰算法。 调零抗干扰算法主要应用功率倒置技术,充分利用导航信号功率低于环境噪声功率的特点&…

ssm汽车在线销售系统

摘 要 21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,管理工作的重要性已逐渐被人们所认识,科学化的管理,使信息存…

Spring Security实现用户认证四:使用JWT与Redis实现无状态认证

Spring Security实现用户认证四:使用JWT与Redis实现无状态认证 1 什么是无状态认证?2 什么是JWT?2.1 需要注意的事项2.2 JWT构成 3 Spring Security JWT实现无状态认证3.1 创建一个Spring Boot项目3.1.1 依赖3.1.2 Main3.1.3 application.ym…

PGFed: Personalize Each Client’s Global Objective for Federated Learning

ICCV-2023, 文章提出显式隐式的概念,作者通过实验发现显式比隐式的效果好,显式方式通过直接与多个客户的经验风险互动来更新模型,并用泰勒展开式降为 O ( N ) O(N) O(N)通讯成本。 文章地址:arxiv code: 作者开源 贡献 1.我们发现个性化 FL 算法的显式性赋予了其更强的…

【Linux】模拟实现一个简单的日志系统

👦个人主页:Weraphael ✍🏻作者简介:目前正在学习c和算法 ✈️专栏:Linux 🐋 希望大家多多支持,咱一起进步!😁 如果文章有啥瑕疵,希望大佬指点一二 如果文章对…

算法体系-20 第二十节暴力递归到动态规划

前言 动态规划模型从尝试暴力递归到傻缓存到动态规划 四种模型和体系班两种模型一共六种模型 0.1 从左往右模型 0.2 范围讨论模型范围尝试模型 (这种模型特别在乎讨论开头如何如何 结尾如何如何) 玩家博弈问题,玩家玩纸牌只能那左或者右 0.3 …

浅析Vue3 实战笔记(一)

本文是结合实践中和学习技术文章总结出来的笔记(个人使用),如有雷同纯属正常((✿◠‿◠)) 喜欢的话点个赞,谢谢! 有问题欢迎指正!! 前面已经讲了基本的Vue生命周期和入门知识,本篇开始使用Vite构建一个demo 1. 创建项目 1.1. 初始化项目 使用Vite初始化项目 yarn create v…

若依RuoYi-Vue分离版—免登录直接访问

若依RuoYi-Vue分离版—免登录直接访问 如何不登录直接访问前端:后端:方法1:在SecurityConfig.java中设置httpSecurity配置匿名访问方法2:在对应的方法或类上面使用Anonymous注解。 如何不登录直接访问 官网有说明:如何不登录直接…

Swift 序列(Sequence)排序面面俱到 - 从过去到现在(二)

概览 在上篇 Swift 序列(Sequence)排序面面俱到 - 从过去到现在(一)博文中,我们讨论了 Swift 语言中序列和集合元素排序的一些基本知识,我们还给出了以自定义类型中任意属性排序的“康庄大道”。 不过在实际的撸码场景中,我们往往需要的是“多属性”同时参与到排序的考…

279. 完全平方数

解法一、回溯法&#xff1a; class Solution {public int numSquares(int n) {return numSquaresHepler(n);}public int numSquaresHepler(int n){if(n 0) return 0;int count Integer.MAX_VALUE;for(int i 1; i * i < n; i){count Math.min(count,numSquaresHepler(n …

elementPlus 图标不显示 属性模式不显示

问题&#xff1a; elementPlus 属性模式图标不显示 <el-input placeholder"请输入用户名" :suffix-icon"Avatar"> //这个图标不显示 之前在main.ts里全局引入了icons-vue。这里的script里也没引入。 解决&#xff1a; 在当前的script中重新引入a…

【Linux】进程_1

文章目录 五、进程1. 冯---诺依曼体系结构2. 操作系统 未完待续 五、进程 1. 冯—诺依曼体系结构 我们常见的计算机和不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系。 冯—诺依曼体系结构由&#xff1a;输入设备、输出设备和中央处理器&#xff…

【C++】——继承(详解)

一 继承的定义和概念 1.1 继承的定义 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保 持原有类特性的基础上进行扩展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类&#xff0c;被继承的称为基类…

CentOS7下快速升级至OpenSSH9.7p2安全版本

一、CentOS7服务器上编译生成OpenSSH9.3p2的RPM包 1、编译打包的shell脚本来源于该项目 https://github.com/boypt/openssh-rpms解压zip项目包 unzip openssh-rpms-main.zip -d /opt cd /opt/openssh-rpms-main/ vim pullsrc.sh 修改第23行为source ./version.env 2、sh pull…