JVM详解——垃圾回收

2023-11-04

垃圾回收

在这里插入图片描述

1、创建对象的过程

  1. JVM遇到字节码new指令时,首先将检查该指令的参数能否在常量池中定位到一个类的符号引用,检查引用代表的类是否已经被加载、解析和初始化,如果没有就执行类加载
  2. 类加载之后,开始创建对象,未对象分配内存空间。判断该对象内存值是否大于-XX:PetenureSizeThreshold​,若大于则直接分配到老年代,若不大于则判断新生代中Eden区(伊甸园)空间是否充足,若充足则分配到Eden区进行下一步,若不足则进行 Minor GC​ 新生代垃圾回收,然后分配内存空间。
  3. 内存分配完成后虚拟机将成员变量设为零值,之后设置对象头(包括哈希码、GC信息、锁信息、对象所属类的类元信息)
  4. 执行 init ​​方法,初始化成员变量执行实例化代码块,调用类的构造方法,并把堆内对象的首地址赋值给引用变量。

2、对象内存分配的方式

在类加载之后,就开始为对象分配内存,根据对象所需内存大小从堆中划分,分配方式主要有两种:指针碰撞空闲列表

  • 指针碰撞: 如果堆内存规整(没有内存碎片)占用的内存在一边,没有占用的内存放在另一边,中间有一个分界指针,分配内存就是把指针向空闲方向挪动一段与对象大小相等的距离。
  • 空闲列表: 如何堆内存不规整,JVM需要维护一个列表记录哪些内存可用,再分配时在列表找到一块足够大的内存进行分配,并更新列表记录。

由此可见,选择哪种分配方式取决于堆空间是否规整,堆空间是否规整由垃圾回收器是否带有压缩整理功能决定。比如 SerialParNew垃圾回收期适用指针碰撞,CMS垃圾回收器使用空闲列表。


3、对象内存分配的线程安全问题

创建对象是非常频繁的事情,如果不能保证线程安全,那么会很容易出现多线程操作同一块内存区域,因此保证线程安全对于虚拟机来说至关重要,虚拟机采用两种方式保证线程安全:

  1. CAS + 失败重试: 保证更新操作的原子性
  2. TLAB本地线程分配缓冲: JVM在Eden区为每个线程分配一个私有缓冲区域,在多线程分配内存时,首先在TLAB中进行分配

JVM优先使用TLAB进行对象内存分配,当对象所需内存大于TLAB剩余空间或TLAB内存耗尽时,才会采用CAS+重试进行内存分配。


4、对象在堆中的生命周期

  1. 当创建一个对象时,对象会被优先分配到新生代的 Eden 区。 JVM 会为对象定义一个对象年龄计数器-XX:MaxTenuringThreshold​)

  2. 当 Eden 空间不足时,JVM 将执行新生代的垃圾回收(Minor GC

    • JVM 会把存活的对象转移到 Survivor​ 中,并且对象年龄 +1
    • 对象在​ Survivor ​​中同样也会经历 Minor GC​,每经历一次 Minor GC​,对象年龄都会+1,如果超过一定次数就会被放入老年代。
  3. 如果分配的对象超过了-XX:PetenureSizeThreshold​,对象会直接被分配到老年代

  4. 在老年代中存放的对象当进行Major GC时被回收,或者Full GC时被回收。


5、垃圾回收机制

JVM为Java程序提供自动内存管理,包括为对象分配内存回收分配给对象的内存。而垃圾回收机制就是JVM回收分配给对象的内存的过程。

在系统运行的过程中,随着时间的推移一些对象会变成无用的,这些对象占据一定的内存,如果不及时对这些对象占用的内存进行回收再利用,内存会逐渐被耗尽。

垃圾回收机制包括三个方面:判断对象是否可回收、对可回收的对象进行回收、如何更好的进行垃圾回收


6、判断一个对象是否可以回收

在进行垃圾回收时,首先需要判断一个对象是否可以被回收,有两种方法引用计数算法可达性分析算法

  1. 引用计数算法:为对象设置一个引用计数器,当对象增加一个引用时计数器加1,引用失效时计数器-1,引用计数为0的对象可以被回收。如果两个对象相会引用就会出现循环引用问题,两个对象无法被回收。
  2. 可达性分析算法:基本思想通过一系列 GC Roots对象作为起点,从这些节点开始向下搜索,节点走过的路径成为引用链,当一个对象到GC Roots没有任何引用链时也就是不可达,那么就说明该对象可以被回收。

JVM使用可达性分析算法判断对象是否可以被回收

GC Roots 对象都有哪些?

栈中引用的对象(包括虚拟机栈和本地方法栈)、方法区中类静态属性引用的对象、方法区中常量引用的对象


7、垃圾回收算法

1、标记-清除

标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。
在这里插入图片描述

不足:

  • 标记和清除过程效率都不高;
  • 会产生大量的内存碎片,导致无法给之后的大对象分配内存。

2、标记-复制

将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。
在这里插入图片描述

主要不足是只使用了内存的一半。

大多虚拟机采用这种收集算法来回收新生代,但是并不是将新生代划分为大小相等的两块,而是分为一块较大的 Eden 空间两块较小的 Survivor 空间,每次使用 Eden 空间和其中一块 Survivor。在回收时,将 Eden 和 Survivor 中还存活着的对象一次性复制到另一块 Survivor 空间上,最后清理 Eden 和使用过的那一块 Survivor。

HotSpot 虚拟机的 Eden 和 两个Survivor 的大小比例默认为 8:1:1,保证了内存的利用率达到 90%。如果每次回收有多于 10% 的对象存活,那么一块 Survivor 空间就不够用了,此时需要依赖于老年代进行分配担保,也就是借用老年代的空间存储放不下的对象。

3、标记-整理

标记所有不需要回收的对象,让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
在这里插入图片描述


8、内存分配策略

  1. 对象优先在 Eden 分配

大多数情况下,对象在新生代 Eden 区分配,当 Eden 区空间不够时,发起 Minor GC

  1. 大对象直接进入老年代

大对象是指需要连续内存空间的对象,典型的大对象是那种很长的字符串以及数组。

经常出现大对象会提前触发垃圾收集以获取足够的连续空间分配给大对象。

-XX:PretenureSizeThreshold​,大于此值的对象直接在老年代分配,避免在 Eden 区Survivor 区之间的大量内存复制。

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

为对象定义年龄计数器,对象在 Eden 出生并经过 Minor GC 依然存活,将移动到 Survivor 中,年龄就增加 1 岁,增加到一定年龄则移动到老年代中。

-XX:MaxTenuringThreshold​ 用来定义年龄的阈值。

  1. 动态对象年龄判定

虚拟机并不是永远地要求对象的年龄必须达到​ MaxTenuringThreshold​ 才能晋升老年代,如果在Survivor​中相同年龄所有对象大小的总和大于 Survivor ​​空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold ​​中要求的年龄。

  1. 空间分配担保

在发生​ Minor GC ​​之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果成立的话,那么Minor GC​可以确认是安全的。

如果不成立虚拟机会查看 HandlePromotionFailure​ 设置值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC​,如果小于或者​ HandlePromotionFailure ​​设置不允许担保失败,那么就要进行一次 Full GC


9、Full GC 的触发条件

  1. 调用 System.gc()

只是建议虚拟机执行 Full GC,但是虚拟机不一定真正去执行。不建议使用这种方式,而是让虚拟机管理内存。

  1. 老年代空间不足

老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等。

为了避免以上原因引起的 Full GC,应当尽量不要创建过大的对象以及数组。除此之外,可以通过-Xmn虚拟机参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过-XX:MaxTenuringThreshold调大对象进入老年代的年龄,让对象在新生代多存活一段时间。

  1. 空间分配担保失败

使用复制算法的 Minor GC 需要老年代的内存空间作担保,如果担保失败会执行一次Full GC

  1. Concurrent Mode Failure

执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足(可能是 GC 过程中浮动垃圾过多导致暂时性的空间不足),便会报​ Concurrent Mode Failure ​​错误,并触发​ Full GC​。


10、故障排查工具

  1. jps : JDK提供的查看当前Java进程的工具
  2. jstack: JDK提供的线程堆栈分析工具,可以查看或导出Java应用程序中线程堆栈信息
  3. jinfo: JDK 自带的命令,可以用来查看正在运行的 java 应用程序的扩展参数,包括Java System属性JVM命令行参数;也可以动态的修改正在运行的 JVM 一些参数。当系统崩溃时,jinfo可以从core文件里面知道崩溃的Java应用程序的配置信息。
  4. jmap: 生成Java程序的dump文件,也可以查看堆内对象示例的统计信息、查看 ClassLoader 的信息
  5. jstat: 虚拟机统计信息监视工具,用于监视虚拟机运行状态信息,可以显示本地或远程虚拟机进程中的类加载、内存、垃圾收集、即时编译器等运行时数据。


参考文章:

  1. https://pdai.tech/md/java/jvm/java-jvm-gc.html
  2. https://javaguide.cn/java/jvm/jvm-garbage-collection.html
  3. https://blog.csdn.net/weixin_45629285/article/details/128050932
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JVM详解——垃圾回收 的相关文章

随机推荐

  • tp5.0 api 接口设计语言包切换功能

    tp5 0 lang 使用 header 传参 语言包没有调用 一 设置语言切换配置 参考文档 https www kancloud cn manual thinkphp5 118132 找到你config设置文件 进行设置 二 设置对应语
  • 网站防御cdn和高防服务器,高防IP和高防CDN哪个防护更好?

    现在网络攻击越来越多 而且有越来越凶猛的趋势 为了保障服务器的安全性和稳定性 不少企业和站长都选了高防服务器 虽然防御提升了 但是攻击依旧是无法避免的 如果使用了高防服务器之后 还不能很好的防御 又不想更换防御更高的高防服务器 不妨可以添加
  • 解决ARM-Compiler ‘Default Compiler Version 5‘ which is not available

    新版MDK不再自带ARM Compiler version 5编译器 如果编辑之前的工程会提示 ARM Compiler Default Compiler Version 5 which is not available 错误 需要手动安装
  • JAR包在CenOS启动成功却打不开项目

    开始看防火墙 service iptables status 提示 Redirecting to bin systemctl status iptables service Unit iptables service could not b
  • RJ45网口灯的含义及当前问题描述

    当前使用的网口是10M 100M的 上有两个灯 绿色和黄色灯 绿色灯状态表示的是网口的连接状态 如果绿色灯常亮表明的是网口处于正常连接状态 黄色灯闪烁代表数据传输 常见的异常 两个灯都不亮 网口未连接成功 两个灯都亮 且常亮 但是数据不通
  • docker入门,这一篇就够了。

    Docker入门 这一篇就够了 Docker容器虚拟化平台 前言 接触docker很长时间了 但是工作中也没有用到 所以总是学了忘 忘了学 不过这次 我打算跟大家分享一下我的学习历程 也算是我的独特的复习笔记 我会在这一篇中讲清楚docke
  • Unity播放视频(一) VideoPlayer的使用

    1 在UI上添加脚本 添加UITexture脚本 用于显示视频 添加VideoPlayer 2 上代码 VideoPlayer m Video UITexture m Texture void Start m Video started O
  • 利用html设置嵌套式表格,表格嵌套 HTML

    嵌套表格就是在一个大的表格中 再嵌进去一个或几个小的表格 即插入到表格单元格中的表格 如果用一个表格布局页面 并希望用另一个表格组织信息 则可以插入一个嵌套表格 表格的嵌套一方面是为使页面的外观更为漂亮 利用表格嵌套来编辑出复杂而精美的效果
  • 3个步骤就让一个web服务器建立起来(web服务器简单写法)

    第一步 新建一个js文件 文件名自行设置 不建议使用中文和数字 内容如下 1 引入http模块 const http require http 2 创建服务 const server http createServer function r
  • 使用百度地图api功能显示位置

  • ctfshow--web入门(web1-web20)

    web1 查看源代码 发现flag web2 查看源代码 通过在url头部添加 view source 或者f12 web3 flag在响应包里 web4 进入robots txt 发现后台遗留文件 web5 考点phps文件泄露 直接访问
  • TypeScript之类与抽象类

    前言 记录一下typeScript中的类 TS中的类与ECMAScript中的类还是有很大区别的 ES6开始引入了类的概念 通过class 关键字定义类 在constructor中定义实例属性等 比起 ES6中的类 在TS中 多了一些类型定
  • 图像噪声

    噪声在理论上可以定义为 不可预测 只能用概率统计方法来描述的随机误差0 因此 可以将图像噪声看成是多维随机过程 描述噪声完全可以借用随机过程及其概率密度函数 数字图像的噪声主要来源于图像的获取和传输过程 图像传感器的工作情况受各种因素影响
  • Vue教程(五):样式绑定——class和style

    1 样式代码准备 样式提前准备
  • org.hibernate.exception.GenericJDBCException: Cannot open connection

    导入一个基于Hibernate的项目后debug 结果出现这个问题 出现这个错误的原因及解决方法 1 数据库没启动 这时你可以简单检验一下 可以拿一个你肯定没问题的项目运行一下 看数据库里表的数据 我用的这个 也可以可以打开数据库客户端 如
  • python 用户信息管理系统【各个函数剖析 + 完整代码 零基础适用篇】

    这个用户管理系统小白也能轻松掌握 只用了函数 用户信息只有两个 姓名 账号 和密码 采用字典存放数据 字典的键即为姓名 值为密码 功能分为两大部分 第一部分为用户的登录和注册 第二部分为管理员对信息的增删查改操作 以下为文章目录 文章目录
  • 【leetcode系列】python单链表查找中间节点

    python单链表查找中间节点 使用快慢指针法 coding UTF 8 class Node def init self data next self data data self next next n1 Node n1 None n2
  • 【满分】【华为OD机试真题2023 JAVA&JS】红黑图

    华为OD机试真题 2023年度机试题库全覆盖 刷题指南点这里 红黑图 知识点枚举 时间限制 1s 空间限制 256MB 限定语言 不限 题目描述 众所周知红黑树是一种平衡树 它最突出的特性就是不能有两个相邻的红色节点 那我们定义一个红黑图
  • 在C/C++中调用Java代码

    JNI就是Java Native Interface 即可以实现Java调用本地库 也可以实现C C 调用Java代码 从而实现了两种语言的互通 可以让我们更加灵活的使用 通过使用JNI可以从一个侧面了解Java内部的一些实现 本文使用的环
  • JVM详解——垃圾回收

    文章目录 垃圾回收 1 创建对象的过程 2 对象内存分配的方式 3 对象内存分配的线程安全问题 4 对象在堆中的生命周期 5 垃圾回收机制 6 判断一个对象是否可以回收 7 垃圾回收算法 8 内存分配策略 9 Full GC 的触发条件 1