【JVM】Java虚拟机(二)——垃圾回收

目录

一、如何判断对象可以回收

(一)引用计数法

(二)可达性分析算法

二、垃圾回收算法

(一)标记清除

(二)标记整理

(三)复制

(四)分代垃圾回收

(五)相关 VM 参数

三、垃圾回收器

(一)串行(Serial)

(二)吞吐量优先

(三)响应时间优先

(四)G1

核心特点

(一)G1 垃圾回收阶段

(二)核心阶段详解

(1) 年轻代GC(Young GC)

(2) 并发标记周期

(3) 混合回收(Mixed GC)

(三)G1关键调优参数

基础配置

区域配置

并发周期控制

四、垃圾回收调优

(一)调优领域

(二)确定目标

(三)最快的 GC

(四)新生代调优

(五)老年代调优


一、如何判断对象可以回收

(一)引用计数法

给对象中添加一个引用计数器:

  • 每当有一个地方引用它,计数器就加 1;
  • 当引用失效,计数器就减 1;
  • 任何时候计数器为 0 的对象就是不可能再被使用的。

这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间循环引用的问题。

所谓对象之间的相互引用问题,如下面代码所示:除了对象 objAobjB 相互引用着对方之外,这两个对象之间再无任何引用。但是他们因为互相引用对方,导致它们的引用计数器都不为 0,于是引用计数算法无法通知 GC 回收器回收他们。

public class ReferenceCountingGc {Object instance = null;public static void main(String[] args) {ReferenceCountingGc objA = new ReferenceCountingGc();ReferenceCountingGc objB = new ReferenceCountingGc();objA.instance = objB;objB.instance = objA;objA = null;objB = null;}
}

(二)可达性分析算法


Java通过GC Roots对象作为起点,向下搜索引用链。若对象与GC Roots无引用链相连,则判定为可回收。


GC Roots包括

  • 虚拟机栈中引用的对象(如局部变量)

  • 方法区中类静态属性引用的对象

  • 方法区中常量引用的对象

  • 本地方法栈中JNI引用的对象

  • 同步锁持有的对象

  • 活跃线程对象

  • 引用类型与回收策略

    引用类型回收条件示例
    强引用永不回收(除非显式置为nullObject obj = new Object()
    软引用(Soft)内存不足时回收SoftReference<Object> ref
    弱引用(Weak)下次GC必然回收WeakReference<Object> ref
    虚引用(Phantom)仅用于回收跟踪PhantomReference<Object> ref
  • 对象死亡过程

1. 强引用

  • 只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收

2. 软引用(SoftReference)

  • 仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象
  • 可以配合引用队列来释放软引用自身

3. 弱引用(WeakReference)

  • 仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
  • 可以配合引用队列来释放弱引用自身

4. 虚引用(PhantomReference)

  • 必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队, 由 Reference Handler 线程调用虚引用相关方法释放直接内存

5. 终结器引用(FinalReference)

  • 无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象 暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize 方法,第二次 GC 时才能回收被引用对象

二、垃圾回收算法

(一)标记清除

定义: Mark Sweep

  • 速度较快
  • 会造成内存碎片

(二)标记整理

定义:Mark Compact

  • 速度慢
  • 没有内存碎片

(三)复制

定义:Copy

  • 不会有内存碎片
  • 需要占用双倍内存空间

(四)分代垃圾回收

  • 年轻代(Young Generation)

    • 对象分配:新对象在Eden区创建

    • 回收算法:复制算法(Minor GC)

    • 过程

      1. Eden(伊甸园) + S0(From)存活对象复制到S1(To)

      2. 清空Eden(伊甸园)和S0(From)

      3. S0(From)与S1(To)角色交换

    • 晋升条件:对象年龄(经历GC次数)达到阈值(默认15),或Survivor区空间不足

  • 老年代(Old Generation)

    • 存放长期存活对象

    • 回收算法:标记-清除或标记-整理(Major GC/Full GC)

    • 触发条件:年轻代晋升失败或空间不足

  • 对象首先分配在伊甸园区域
  • 新生代空间不足时,触发 Minor GC,伊甸园和 From 存活的对象使用copy复制到 To 中,存活的对象年龄加1并且交换 From 和 To
  • Minor GC 会引发 Stop The World,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行
  • 当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit)
  • 当老年代空间不足,会先尝试触发 Minor GC,如果之后空间仍不足,那么触发 Full GC,STW的时间更长

算法原理优点缺点
标记-清除1. 标记所有活动对象
2. 清除未标记对象
实现简单内存碎片化,分配效率低
复制内存分为两块,每次使用一块。GC时将存活对象复制到另一块,清空当前块。无碎片,高效内存利用率仅50%
标记-整理1. 标记活动对象
2. 将存活对象向内存一端移动
3. 清理边界外内存
无碎片,内存利用率高对象移动开销大
分代收集结合多种算法,按对象生命周期分区管理综合性能最优(主流方案)实现复杂

(五)相关 VM 参数

含义参数
堆初始大小-Xms
堆最大大小-Xmx 或 -XX:MaxHeapSize=size
新生代大小-Xmn 或 (-XX:NewSize=size + -XX:MaxNewSize=size )
幸存区比例(动态)-XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy
幸存区比例-XX:SurvivorRatio=ratio
晋升阈值-XX:MaxTenuringThreshold=threshold
晋升详情-XX:+PrintTenuringDistribution
GC详情-XX:+PrintGCDetails -verbose:gc
FullGC 前 MinorGC-XX:+ScavengeBeforeFullGC

三、垃圾回收器

1. 串行(Serial)

  • 单线程
  • 堆内存较小,适合个人电脑

2. 吞吐量优先(Parallel Scavenge)

  • 多线程
  • 堆内存较大,多核 cpu
  • 让单位时间内,STW 的时间最短 0.2 0.2 = 0.4,垃圾回收时间占比最低,这样就称吞吐量高

3. 响应时间优先(CMS)

  • 多线程
  • 堆内存较大,多核 cpu
  • 尽可能让单次 STW 的时间最短 0.1 0.1 0.1 0.1 0.1 = 0.5

(一)串行(Serial)

-XX:+UseSerialGC = Serial + SerialOld

(二)吞吐量优先(Parallel Scavenge)

-XX:+UseParallelGC ~ -XX:+UseParallelOldGC

-XX:+UseAdaptiveSizePolicy

-XX:GCTimeRatio=ratio

-XX:MaxGCPauseMillis=ms

-XX:ParallelGCThreads=n

(三)响应时间优先(CMS)

-XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~ SerialOld

-XX:ParallelGCThreads=n ~ -XX:ConcGCThreads=threads

-XX:CMSInitiatingOccupancyFraction=percent

-XX:+CMSScavengeBeforeRemark

(四)G1

G1 (Garbage-First) 是Java 7引入、Java 9成为默认的垃圾回收器,专为大内存、低延迟场景设计,替代了传统的CMS回收器。

核心特点

  • 分区模型:将堆划分为多个等大小的Region(默认约2048个)

  • 分代收集:保留新生代/老年代概念,但物理不连续

  • 可预测停顿:通过-XX:MaxGCPauseMillis设置目标停顿时间

  • 并发标记:与应用程序线程并行工作

  • 混合回收:同时清理新生代和老年代区域

(一)G1 垃圾回收阶段

Young Collection

  • 会 STW

Young Collection + CM

  • 在 Young GC 时会进行 GC Root 的初始标记
  • 老年代占用堆空间比例达到阈值时,进行并发标记(不会 STW),由下面的 JVM 参数决定

-XX:InitiatingHeapOccupancyPercent=percent (默认45%)

Mixed Collection

会对 E、S、O 进行全面垃圾回收

  • 最终标记(Remark)会 STW
  • 拷贝存活(Evacuation)会 STW

-XX:MaxGCPauseMillis=ms

(二)核心阶段详解

(1) 年轻代GC(Young GC)
  • 触发条件:Eden区满时

  • 过程

    1. STW(Stop-The-World)开始

    2. 构造回收集(Collection Set = Eden + Survivor)

    3. 复制存活对象到新Survivor区

    4. 年龄达到阈值(默认15)的对象晋升老年代

(2) 并发标记周期
  • 触发条件:老年代占用超过IHOP阈值(默认45%)

  • 阶段组成

    • 初始标记(STW):伴随Young GC进行

    • 根区域扫描:扫描Survivor区引用

    • 并发标记:标记存活对象(与应用并发)

    • 最终标记(STW):处理SATB缓冲区

    • 清理(STW):统计完全空闲Region

(3) 混合回收(Mixed GC)
  • 回收包含年轻代Region + 高收益的老年代Region

  • 根据垃圾比例排序Region(Garbage-First原则)

  • 多次执行直到满足回收目标

(三)G1关键调优参数

基础配置
参数默认值说明
-XX:+UseG1GC-启用G1回收器
-Xms / -Xmx-堆初始/最大大小(建议设相同值)
-XX:MaxGCPauseMillis200ms目标最大停顿时间
区域配置
参数默认值说明
-XX:G1HeapRegionSize1-32MBRegion大小(2的幂次)
-XX:G1NewSizePercent5%新生代最小占比
-XX:G1MaxNewSizePercent60%新生代最大占比
并发周期控制
参数默认值说明
-XX:InitiatingHeapOccupancyPercent45%IHOP阈值(触发并发标记)
-XX:G1MixedGCLiveThresholdPercent85%Region存活对象低于此值才回收
-XX:G1HeapWastePercent5%可回收垃圾占比阈值(停止Mixed GC)

四、垃圾回收调优

(一)调优领域

  1. 内存
  2. 锁竞争
  3. cpu
  4. 占用 io

(二)确定目标

  • 【低延迟】还是【高吞吐量】,选择合适的回收器
  • CMS,G1,ZGC
  • ParallelGC
  • Zing

(三)最快的 GC

答案是不发生 GC

查看 FullGC 前后的内存占用,考虑下面几个问题

数据是不是太多?

  • resultSet = statement.executeQuery("select * from 大表 limit n")

数据表示是否太臃肿?

  • 对象图
  • 对象大小 16 Integer 24 int 4

是否存在内存泄漏?

  • static Map map =
  • 第三方缓存实现

(四)新生代调优

  • 新生代的特点

    • 所有的 new 操作的内存分配非常廉价
      • TLAB thread - local allocation buffer
    • 死亡对象的回收代价是零
    • 大部分对象用过即死
    • Minor GC 的时间远远低于 Full GC
  • 越大越好吗?

  • 新生代能容纳所有【并发量*(请求 - 响应)】的数据

  • 幸存区大到能保留【当前活跃对象 + 需要晋升对象】

  • 晋升阈值配置得当,让长时间存活对象尽快晋升

    • -XX:MaxTenuringThreshold = threshold
    • -XX:+PrintTenuringDistribution
    Desired survivor size 48286924 bytes, new threshold 10 (max 10)age 1: 28992024 bytes, 28992024 total
    age 2: 1366864 bytes, 30358888 total
    age 3: 1425912 bytes, 31784800 total …

(五)老年代调优

以 CMS 为例

  • CMS 的老年代内存越大越好
  • 先尝试不做调优,如果没有 Full GC 那么已经…,否则先尝试调优新生代
  • 观察发生 Full GC 时老年代内存占用,将老年代内存预设调大 1/4 ~ 1/3
    • -XX:CMSInitiatingOccupancyFraction=percent

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

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

相关文章

Android 实现可拖动的ImageView

Android 实现可拖动的ImageView 代码实现&#xff1a; public class DraggableImageView extends AppCompatImageView {private float lastTouchX;private float lastTouchY;public DraggableImageView(Context context) {super(context);init();}public DraggableImageView(C…

微信小程序中wxs

一、先新建wxs文件subutil.wxs 1、写过滤器 //return class var isClass function(val) {if (val 0) {return grid-item} else if (val 1) {return temperature-error-slot} else if (val 2) {return chargingCycles-error-slot} else {return unrecognized-slot} } 2、…

Nginx攻略

&#x1f916; 作者简介&#xff1a;水煮白菜王&#xff0c;一位前端劝退师 &#x1f47b; &#x1f440; 文章专栏&#xff1a; 前端专栏 &#xff0c;记录一下平时在博客写作中&#xff0c;总结出的一些开发技巧和知识归纳总结✍。 感谢支持&#x1f495;&#x1f495;&#…

常见系统设计

秒杀系统 前端层&#xff1a; 静态资源缓存&#xff1a;通过CDN缓存商品图片、页面静态HTML&#xff0c;减少回源请求。 请求合并&#xff1a;合并用户频繁刷新的请求&#xff08;如10秒内仅允许一次真实请求&#xff09;。 端侧限流&#xff1a;通过JS或APP端限制用户高频点击…

git撤回commit

最常见的几种撤回方式&#xff1a; 目标使用命令是否保留修改撤回最后一次 commit&#xff0c;但保留代码修改git reset --soft HEAD~1✅ 保留撤回最后一次 commit&#xff0c;并丢弃修改git reset --hard HEAD~1❌ 丢弃撤回某个 commit&#xff0c;但保留后续提交git revert …

docker 安装运行mysql8.4.4

先前一直使用mysql5.7&#xff0c;最新公司新项目&#xff0c;无意翻阅看下5.x版本mysql官方已经不再支持&#xff0c;于是准备选用MySQL8&#xff0c;官方8.4版本是个长期支持版本&#xff0c;选则最新版本8.4.4&#xff0c;如下是MySQL官方对版本支持计划 MySQL版本下载查看地…

[java八股文][MySQL面试篇]索引

索引是什么&#xff1f;有什么好处&#xff1f; 索引类似于书籍的目录&#xff0c;可以减少扫描的数据量&#xff0c;提高查询效率。 如果查询的时候&#xff0c;没有用到索引就会全表扫描&#xff0c;这时候查询的时间复杂度是On如果用到了索引&#xff0c;那么查询的时候&a…

低代码平台的版本管理深度解析

引言 在当今快速发展的软件开发领域&#xff0c;低代码平台凭借其可视化界面和拖拽功能&#xff0c;极大地减少了手动编码的工作量&#xff0c;显著提高了开发效率和质量。它提供了丰富的预构建模块、组件和服务&#xff0c;让开发者能够根据业务需求和逻辑进行组合与配置&…

Springboot项目由JDK8升级至JDK17全过程教程【文末附源码】

1. 前言 最近一直想把我的开源项目maple-boot升级到jdk17版本&#xff0c;然后接入Spring AI。拖延症犯了一直拖拖拖&#xff0c;最近时间空闲较多&#xff0c;开始陆续着手升级。 整个升级过程 计划分为3步。 step1&#xff1a;先将项目升级到jdk17&#xff0c;使用正常ste…

同步与异步:软件工程中的时空艺术与实践智慧-以蜻蜓hr人才系统举例-优雅草卓伊凡

同步与异步&#xff1a;软件工程中的时空艺术与实践智慧-以蜻蜓hr人才系统举例-优雅草卓伊凡 概念解析&#xff1a;时空维度的编程范式 在软件开发的宇宙中&#xff0c;同步(Synchronous)与异步(Asynchronous)是两种根本不同的执行模式&#xff0c;它们塑造了程序与时间和空间…

TF-IDF算法的代码实践应用——关键词提取、文本分类、信息检索

回顾&#xff1a;TF-IDF算法详解与实践总结 上一篇文章我们深入剖析了TF-IDF的原理与细节&#xff0c;但实践才是检验真理的唯一标准&#xff01;今天&#xff0c;我们将从“纸上谈兵”转向“实战演练”&#xff1a;通过纯Python手写实现与调用sklearn工具包两种方式&#xff0…

前端面试宝典---事件循环面试题

浏览器进程模型与 JavaScript 执行机制 现代浏览器采用多进程架构&#xff0c;包含浏览器进程、渲染进程、网络进程等多个核心进程。每个标签页会独立创建一个渲染进程&#xff0c;负责页面内容的解析、渲染和执行脚本代码。 JavaScript 的单线程特性 JavaScript 采用单线程…

postman调用接口报错401, Unauthorized, Invalid Token. null解决办法

1、先登录系统&#xff0c;F12找到token并复制 2、postman里选中Authorization,下拉选中选择Bearer Token,把复制好的token黏贴到右侧输入框&#xff0c;如下所示&#xff1a; 3、如果是json格式的参数拷贝到Body中&#xff0c;如下所示&#xff1a; 4、 接口调用成功

C++----剖析list

前面学习了vector和string&#xff0c;接下来剖析stl中的list&#xff0c;在数据库中学习过&#xff0c;list逻辑上是连续的&#xff0c;但是存储中是分散的&#xff0c;这是与vector这种数组类型不同的地方。所以list中的元素设置为一个结构体&#xff0c;将list设计成双向的&…

为什么已经有 Nginx 了,还需要服务网关?

在当前微服务架构中&#xff0c;虽然 Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;但在实际使用中仍然存在诸多局限性。为了满足运维效率、功能统一治理以及与微服务生态集成的需求&#xff0c;通常会在 Nginx 和业务服务之间引入一层基于 Java 实现的服务网关&a…

Kendo UI 中,ViewModel、DataSource 和 Grid的关系。Kendo 框架发起 HTTP 请求

Kendo UI 中&#xff0c;ViewModel、DataSource 和 Grid的关系 在 Kendo UI 中&#xff0c;ViewModel、DataSource 和 Grid 是构建动态数据应用的核心组件&#xff0c;三者协同工作实现数据的绑定、管理和展示。 一、三者关系图解 #mermaid-svg-3lWxu2zWB23wDYEz {font-family…

宇树开源 Qmini 双足机器人,可通过 3D 打印动手制作,使用树莓派作为主控制器

Unitree Qmini 是一款由宇树科技设计并开源的低成本双足机器人&#xff0c;开发者可以完全通过 3D 打印进行复刻。Qmini 专为业余爱好者、教育工作者和研究人员设计&#xff0c;使用户能够快速上手&#xff0c;并以类似乐高的模块化方式组装自己的机器人。该项目为机器人技术提…

解决华为云服务器无法ping通github问题

在push代码到github上的时候&#xff0c;发现显示22端口无法连接&#xff0c;在已经开放了端口&#xff0c;防火墙关闭的情况下仍然无法连接到GitHub。 发现是服务器和github断连&#xff0c;选择 sudo vim /etc/hosts 添加一下代码 # GitHub Start140.82.121.4 gith…

关于electron-vite koffi 读取 dll 打包等问题得记录

koffi const koffi require(‘koffi’) import iconv from ‘iconv-lite’;const libPath path.resolve(__dirname, ‘…/…/resources/dll/sss.dll’) const yktLib koffi.load(libPath) const ret yktLib.func(‘string sss(string Url, string Data, string OutData)’…

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…