JVM中的对象和引用详解

2023-11-20

1. 对象的创建过程

在这里插入图片描述
其中的步骤是:当虚拟机碰到一条new指令时,先检查对象是否被加载,如果未被加载,就先将class加载到运行时数据区;然后虚拟机为对象分配内存,分配内存有两种方式:

  • 内存空间如果不是碎片化的,内存中已经使用的和未使用的内存空间之间就有个指示器指针,分配内存就是挪动指示器的过程,这种方式称为指针碰撞(简单高效);

  • 内存空间如果是碎片化的,虚拟机就会维护一张记录哪些空间是空闲可用的,分配内存的过程就是更新这张列表的过程,这种方式称之为空闲列表(复杂)。
    分配内存的方式由垃圾回收器是否做内存整理来决定。

分配内存中需要考虑的重要一点就是并发安全,对象的创建时很频繁的,对内存空间的分配管理需要考虑并发安全的问题,解决这个问题有两个方案:

  • CAS机制:利用CAS机制加上失败重试的方式保证内存分配操作的原子性;
  • 本地线程分配缓冲(TLAB):在每个线程初始化时,JVM会给他分配一块堆空间中的内存,这样每个线程分配内存就在本地进行,如果内存不够就继续向Eden区申请,这样做效率高而且不会引发并发安全问题。
    参数:XX:+UseTLAB 允许在Eden区申请TLAB,默认开启

内存分配完,就是对内存空间进行初始化,这是一个赋零值的过程,之后虚拟机要对对象进行设置
在这里插入图片描述
这样子,一个对象就产生了,虽然所有值都为“零”,但是咱们秃头将军们可以来“玩”她们了,咱们的new指令就可以根据构造方法来对他们进行初始化了。

2. 对象的访问

对象创建出来之后,咱们就可以通过栈上的 reference 数据来操作堆上的具体对象了,访问一般有两种方式:句柄和直接指针

  1. 句柄:java堆中有一块区域叫做句柄池,句柄池存储的是对象的句柄信息,其中包括对象实例数据和类型数据各自的具体地址信息,reference存储的就是对象的句柄地址。这种方式的好处就是即使对象在堆中频繁地移动,改变的也只是句柄中的实例数据指针,而对reference数据不影响;
  2. 直接指针: 这种方式reference中存储的就是对象的内存地址;
  3. 对比:这两种方式各有优缺点,其中直接指针优点就是访问速度快,小缺点是对象实例数据频繁移动,维护这个直接内存地址也需要开销;句柄的优点就是稳定,但是要在堆中创建维护一个句柄池,这样访问性能可能会稍差。

3. 判断对象的存活

2.1 引用计数法: 在对象中添加一个引用计数器,每当有地方引用他就+1,引用失效就-1。但是遇到循环引用问题时需要引入额外的机制来处理,影响效率,python目前用的就是这种;
2.2 可达性分析算法: 以GCRoot为起点,开始往下搜索,这个过程中的路径成为引用链,当一个对象没有和引用链相连时,他就被判定是垃圾。
可作为GCRoot的对象(主要是前四种)
2.2.1. 虚拟机栈(栈帧中的本地变量表)中引用的对象;各个线程调用方法堆栈中使用到的参数、局部变量、临时变量等;
2.2.2. 方法区中类静态属性引用的对象;java 类的引用类型静态变量;
2.2.3. 方法区中常量引用的对象;比如:字符串常量池里的引用;
2.2.4. 本地方法栈中 JNI(即一般说的 Native 方法)引用的对象;

2.2.5. JVM 的内部引用(class 对象、异常对象 NullPointException、OutofMemoryError,系统类加载器);
2.2.6. 所有被同步锁(synchronized 关键)持有的对象;
2.2.7. JVM 内部的 JMXBean、JVMTI 中注册的回调、本地代码缓存等;
2.2.8. JVM 实现中的“临时性”对象,跨代引用的对象。

4. finalize方法

在根可达算法判定为垃圾的对象,再经过一次标记筛选之后就会被GC回收,而在这之间,如果对这个对象执行了finalize()对他进行了一次"起死回生",GC就不会回收它,但是finalize()只能拯救它一次,也就说如果再将他指向空,再对他执行finalize(),它依旧会被GC回收。这个方法不可靠,不建议使用

5. 引用

  • 强引用:就普通的Object obj = new Object();就是强引用,只要引用在,GC就不会回收;
  • 软引用(SoftReference): 内存即将溢出时,软引用对象会被回收,一般是用在一些占用内存空间且使用次数少的对象;
  • 弱引用(WeakReference):下一次GC时,弱引用对象会被回收,和软引用一样用在内存资源紧张的情况下以及创建不是很重要的数据缓存;
  • 虚引用(PhantomReference):随时会被回收,垃圾回收的时候收到一个通知,主要作用就是为了监控垃圾回收器是否正常工作

6. 对象的分配策略

6.1. 基本所有的对象都在堆上分配:这里说的是"基本",也就是说还有例外,例外就是在栈上分配,这种分配方式也称为逃逸

6.2. 逃逸分析:调用参数传递到其他方法中,这种称之为方法逃逸,甚至还有可能被外部线程访问到,这种称之为线程逃逸
如果确定一个对象不会逃逸出线程之外,那么让对象在栈上分配内存可以提高 JVM 的效率。

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

6.4. 大对象直接进入老年代
大对象就是指需要大量连续内存空间的 Java 对象,他们会占用大量连续的内存空间,会导致提前触GC,而且在复制对象时会造成很大的内存开销。所以
HotSpot 虚拟机提供了-XX:PretenureSizeThreshold 参数,指定大于该设置值的对象直接在老年代分配,这样做的目的就是避免在 Eden 区及两个 Survivor 区之间来回复制,产生大量的内存复制操作。

6.5. 长期存活对象进入老年区
对象头中MarkWord记录了对象GC分代年龄,对象在eden区产生年龄为0,经过一次minorGC进入survivor区,年龄变成1,之后在survivor区每经过一次minorGC年龄就增加1,当到达一定年龄就进入老念代。并发垃圾回收器默认是15,CMS默认是6,参数-XX:MaxTenuringThreshold可以调整。

6.6. 对象年龄动态判定
虚拟机并不是一定要对象年龄到达一定大小才进入老年代,当survivor区所有相同年龄的对象大小总和大于survivor区一半时,年龄大于或等于该年龄的对象就直接进入老年代。

6.7. 空间分配担保
在发生minorGC之前,虚拟机会检查老年代连续可用空间是否大于新生代总空间大小,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次minorGC,尽管这次minorGC是有风险的,如果担保失败则会进行一次FullGC;如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JVM中的对象和引用详解 的相关文章

  • Spring批处理返回自定义进程退出代码

    我有一个包含多个作业的 jar 我只想每次只执行一个作业并检索自定义退出代码 例如 我有基本工作 retrieveErrorsJob 配置 只需一步即可读取输入 XML 文件并将数据写入特定数据库表中 应用类 SpringBootAppli
  • 性能说明:预热后代码运行速度变慢

    下面的代码运行完全相同的计算 3 次 它没有做太多事情 基本上将 1 到 100m 之间的所有数字相加 前 2 个块的运行速度大约是第三个块的 10 倍 我已经运行这个测试程序超过 10 次 结果显示差异很小 如果有的话 我希望第三个块运行
  • Docker 容器中运行的 JVM 的驻留集大小 (RSS) 和 Java 总提交内存 (NMT) 之间的差异

    设想 我有一个 JVM 在 Docker 容器中运行 我使用两种工具进行了一些内存分析 1 top 2 Java 本机内存跟踪 这些数字看起来令人困惑 我正在尝试找出造成差异的原因 问题 Java 进程的 RSS 报告为 1272MB 总
  • 运行JDK代码时Java JIT会作弊吗?

    我正在对一些代码进行基准测试 但我无法让它运行得像java math BigInteger https docs oracle com javase 7 docs api java math BigInteger html 即使使用完全相同
  • JVM CPU 峰值故障排除

    我们在其中一台应用程序服务器上发现了一个有趣的 尽管相当严重 问题 在某个时间点 运行 Web 应用程序的 JVM 的 CPU 使用率开始上升 并持续上升 直到应用程序最终减慢到爬行 修复此问题的唯一方法是重新启动应用程序服务器软件 应用服
  • 在最近的 JVM 中,不可见引用仍然是一个问题吗?

    我正在读书Java 平台性能 http java sun com docs books performance 1st edition html JPAppGC fm html 遗憾的是 自从我最初提出这个问题以来 该链接似乎已经从互联网上
  • Java VM 突然退出且没有明显原因

    我的 Java 程序突然退出 没有抛出任何异常 也没有正常完成 这是一个问题 我正在写一个程序来解决欧拉计划 http projecteuler net s 这就是我得到的 private static final int INITIAL
  • 如果只有完全限定名称,如何获取 java 类的二进制名称?

    反射类和方法以及类加载器等需要使用所谓的 二进制 类名称 问题是 如果只有完全限定名称 即在源代码中使用的名称 如何获得二进制名称 例如 package frege public static class RT public static
  • 如何使用IntelliJ IDEA ThreadDumpVisualizer插件分析Java线程转储

    我正在寻找使用一些线程转储分析器来分析 Java 线程转储并安装了ThreadDumpVisualizerIntelliJ IDEA 插件 但不知道如何使用它 插件页面 https plugins jetbrains com plugin
  • Java 堆被无法访问的对象淹没

    我们的 Java EE 应用程序开始出现一些严重问题 具体来说 应用程序在启动后几分钟内就运行了高达 99 的老年代堆 不会抛出 OOM 但实际上 JVM 没有响应 jstat 显示老年代的大小根本没有减少 没有垃圾收集正在进行 并且kil
  • 哪种语言(在 JVM 上运行)最适合创建 DSL?

    我们需要创建复杂的固定长度和可变长度字符串 这些字符串可能代表客户资料 订单等 你们建议使用哪种基于 JVM 的编程语言 想法是让最终用户使用此 DSL 创建字符串 所以我正在寻找验证 代码完成等 Groovy http docs code
  • 热点 JVM 字节码解释器是跟踪 JIT 吗?

    这个问题几乎说明了一切 我一直在寻找答案 甚至通过 VM 规范 但我没有明确说明 No 不过 还有一些其他 JVM 具有跟踪 JIT HotPath http HotPath GoogleCode Com and Maxine http L
  • Java 接口合成方法生成,同时缩小返回类型

    我有 2 个接口和 2 个返回类型 interface interfaceA Publisher
  • 一个好的 Java VM 中方法调用的开销是多少?

    有人可以提供反汇编的机器代码汇编程序列表吗 我的意思是 与 C 中的普通函数调用相比 肯定有一些开销 VM 需要跟踪调用以查找热点 并且当它使用编译代码时 如果新加载的类需要重新编译 它需要提供动态更改编译方法的方法 我想某处也有返回堆栈溢
  • 监控 Java 应用程序上的锁争用

    我正在尝试创建一个小基准 在 Groovy 中 以显示几个同步方法上的高线程争用 当监控自愿上下文切换时 应该会出现高争用 在 Linux 中 这可以通过 pidstat 来实现 程序如下 class Res private int n s
  • JVM内存段分配

    好吧 我有一个关于 JVM 内存段的问题 我知道每个 JVM 都会选择稍微不同地实现这一点 但这是一个总体概念 在所有 JVM 中应该保持相同 一个在运行时不使用虚拟机执行的标准C C 程序在运行时有四个内存段 代码 堆栈 堆 数据 所有这
  • 如何在Java中计算对象的数字年龄[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我想知道Java中对象的年龄 当我们使用new关键字时 Java中用户定义的对象被创建 但是什么时候它会被销毁 是跨越JVM的perm
  • Java 语言中不可用的字节码功能

    当前 Java 6 是否有一些事情可以在 Java 字节码中完成而在 Java 语言中无法完成 我知道两者都是图灵完备的 所以将 可以做 理解为 可以做得更快 更好 或者只是以不同的方式 我正在考虑额外的字节码 例如invokedynami
  • 抛出 Java 异常时是否会生成堆栈跟踪?

    这是假设我们不调用 printstacktrace 方法 只是抛出和捕获 我们正在考虑这样做是为了解决一些性能瓶颈 不 堆栈跟踪是在构造异常对象时生成的 而不是在抛出异常对象时生成的 Throwable 构造函数调用 fillInStack
  • Kotlin 未解决的参考:CLI 上 gradle 的 println

    放一个printlnkotlin 函数返回之前的语句会崩溃 堆栈跟踪 thufir dur NetBeansProjects kotlin thufir dur NetBeansProjects kotlin gradle clean bu

随机推荐

  • 若依单体版添加免密登录

    若依单体版添加 免密登录 的流程在其官网有流程 若依常见问题地址 可以查询到 如何实现用户免密登录配置方法 具体代码官网都有 记录一下实际操作部分细节 1 LoginService添加login方法 去掉密码验证 可能是若依框架更新频繁 网
  • mysql8.0.18数据恢复方法-ibd文件恢复(innodb引擎)

    1 创建同名数据库 2 创建相同的表 3 使用ALTER TABLE 数据库名 表名 DISCARD TABLESPACE 4 将ibd文件拷贝到数据库的数据目录下 5 使用ALTER TABLE 数据库名 表名 IMPORT TABLES
  • apt-get: command not found

    CentOS5 4 apt get install gcc bash apt get command not found 解答 CentOS的软件安装工具不是apt get 是yum yum y install gcc 在ubuntu下安装
  • 检索 COM 类工厂中 CLSID 为 {} 的组件时失败,原因是出现以下错误: 80040154没有注册类.(注册组件的方法)

    执行程序报错如下 原因 没有注册rmReport组件 解决办法 注册组件 注册组件方法如下 首先你要知道你要注册哪个文件 并且找到它 上图报错位置那个变量是我要注册的文件 并且我在自己的项目中找到了它 之后打开C Windows Syste
  • 仅需一个样本即可定制个性化的SAM

    Personalize Segment Anything Model with One Shot https arxiv org pdf 2305 03048 pdf https github com ZrrSkywalker Person
  • shell脚本之循环语句

    for循环 语法1 for i in 集合 do 程序 done 语法2 for i 0 i lt 100 i do 程序 done eg 批量压缩文件 bin bash 批量压缩文件 cd root ls tar gz gt ls log
  • Flutter屏幕适配之二:Image资源assets的使用,实现不同分辨率图片的适配

    这两天在研究Flutter的屏幕适配方案 关于尺寸大小已经有点眉目了 初步定了利用等比例缩放 感兴趣的朋友可移步Flutter屏幕大小适配 但是还是不清楚怎么进行图片分辨率适配的 在百度了之后 仍然有点云里雾里 罢了 直接上官网看吧 果然
  • 【机器学习】入门:为什么梯度下降算法这么有效?

    译者 张雨佳 人们很难真正通过数学理解东西 你只需要去习惯并使用它 约翰 冯 诺伊曼 在机器学习中 我们已经习惯了使用梯度下降法解决问题 以至于没人去质疑它为什么有效 大家经常将梯度下降法模拟为爬山 要想找到崎岖地形中的顶峰 或低谷 就必须
  • Unity之获取游戏物体对象或组件的几个方法

    文章目录 前言 通过物体名称获取对象 GameObject Find Transform Find 通过物体标签获取对象 GameObject FindWithTag GameObject FindGameObjectWithTag Gam
  • 使用SVD求最小二乘刚性转置

    参见文章 Least Squares Rigid Motion Using SVD 一 问题描述 假设P p1 p2 pn 和Q q1 q2 qn 是两组Rd空间中的对应点集 现在想要根据这个两个点集的数据来计算出它们之间的刚性转置信息 可
  • LINUX下安装软件命令详解

    一 解析Linux应用软件安装包 通常Linux应用软件的安装包有三种 1 tar包 如software 1 2 3 1 tar gz 它是使用UNIX系统的打包工具tar打包的 2 rpm包 如software 1 2 3 1 i386
  • QT遍历文件夹下的所有文件

    文章目录 方法一 代码 方法二 需要注意的地方 代码 技巧 代码 方法一 使用类QDirIterator来进行遍历 简介 大概是说 适合于大目录遍历 支持递归但是不支持排序 QDirIterator NoIteratorFlags默认值 没
  • SpringBoot整合ELK教程

    SpringBoot整合ELK教程 1 基础概念 ELK 即 Elasticsearch Logstash Kibana 组合起来可以搭建线上日志系统 本文主要讲解使用 ELK 来收集测试框架产生的日志 Elasticsearch 用于存储
  • Nim 游戏

    你和你的朋友 两个人一起玩 Nim 游戏 桌子上有一堆石头 你们轮流进行自己的回合 你作为先手 每一回合 轮到的人拿掉 1 3 块石头 拿掉最后一块石头的人就是获胜者 假设你们每一步都是最优解 请编写一个函数 来判断你是否可以在给定石头数量
  • Linux_centos7_文件与目录管理_目录操作(1)_(Bird_Bro)

    关键词 路径 相对 绝对 目录操作 特殊目录 目录处理 环境变量 文件与目录验视 ls https blog csdn net weixin 38872771 title directory operating command 切换至roo
  • 程序员教你如何用Python爬取付费小说

    小说相信大家都爱看吧一章接一章具有极大的吸引力 看了还想看 当然付费小说价格也不便宜 看到一半突然收费 猝不及防 在我们程序员这里 收费是不存在的 万物皆可爬 什么是网络爬虫 网络爬虫 又被称为网页蜘蛛 网络机器人 在FOAF社区中间 更经
  • 【Colab】【使用外部数据的7种方法】

    文章目录 方法1 通过Files explorer上传 方法2 使用Colab files上传 方法3 读取Github链接 方法4 克隆Github项目 方法5 使用wget axel下载文件 方法6 读取谷歌硬盘数据 方法7 Kaggl
  • 天干年月算法java

    上班空闲时间 看见朋友圈发了张毛笔画 上面写了丙申 好奇查了查 下面说说java实现 很简单 简单到爆 哈啊哈 String tianGanTZ 庚 辛 壬 癸 甲 乙 丙 丁 戊 己 申 酉 戌 亥 子 丑 寅 卯 辰 巳 午 未 int
  • Java+SSM+Vue 毕业设计 电影院在线售票管理系统系统(含源码+论文)

    文章目录 1 项目简介 2 实现效果 2 1 界面展示 3 设计方案 3 1 概述 3 2 开发环境 3 3 系统流程 3 3 1 系统开发流程 3 3 2 用户登录流程 3 3 3 系统操作流程 3 4 系统结构设计 4 项目获取 1 项
  • JVM中的对象和引用详解

    1 对象的创建过程 其中的步骤是 当虚拟机碰到一条new指令时 先检查对象是否被加载 如果未被加载 就先将class加载到运行时数据区 然后虚拟机为对象分配内存 分配内存有两种方式 内存空间如果不是碎片化的 内存中已经使用的和未使用的内存空