JVM 中的完整 GC 流程

一、引言

在 Java 应用程序的运行过程中,垃圾回收是一个至关重要的环节。它负责自动管理内存,回收不再被使用的对象,以确保应用程序的稳定运行。了解 JVM 中一次完整的 GC 流程对于优化 Java 应用的性能、减少内存占用以及避免内存泄漏至关重要。本文将深入探讨 JVM 中的 GC 流程。

二、JVM 内存结构概述

(一)堆内存

  1. 新生代(Young Generation)
    • Eden 区:新创建的对象首先分配在 Eden 区。
    • Survivor 区:分为 From Survivor 和 To Survivor 两个区域,用于存放经过一次 Minor GC 后仍然存活的对象。
  2. 老年代(Old Generation):存放经过多次 Minor GC 后仍然存活的对象。

(二)方法区(Metaspace)

存储类信息、常量、静态变量等数据。

(三)程序计数器、虚拟机栈和本地方法栈

用于存储线程的执行状态和局部变量等信息。

三、GC 类型

(一)Minor GC

  1. 触发条件
    • 当 Eden 区满时,触发 Minor GC。
  2. 作用范围
    • 主要清理新生代中的垃圾对象。

(二)Major GC/Full GC

  1. 触发条件
    • 老年代空间不足时触发 Major GC 或 Full GC。
    • 永久代(在 Java 8 后被 Metaspace 替代)空间不足时也可能触发 Full GC。
    • 显示调用 System.gc () 时可能触发 Full GC,但不建议在生产环境中使用。
  2. 作用范围
    • 清理整个堆内存,包括新生代和老年代。

四、Minor GC 流程

(一)标记阶段

  1. 可达性分析
    • 从根对象(如线程栈中的局部变量、静态变量等)开始,通过引用链遍历所有可达的对象。
    • 不可达的对象被标记为垃圾。
  2. 三色标记法
    • 白色:表示未被访问过的对象。
    • 灰色:表示对象已经被访问过,但它的引用还没有被完全处理。
    • 黑色:表示对象已经被访问过,并且它的引用也已经被完全处理。

(二)复制阶段

  1. 将 Eden 区和 From Survivor 区中存活的对象复制到 To Survivor 区。
  2. 如果对象的年龄达到一定阈值(默认是 15),则将其晋升到老年代。

(三)清理阶段

  1. 清理 Eden 区和 From Survivor 区中的垃圾对象。
  2. 将 From Survivor 区和 To Survivor 区互换角色,为下一次 Minor GC 做准备。

五、Major GC/Full GC 流程

(一)标记阶段

  1. 与 Minor GC 的标记阶段类似,采用可达性分析和三色标记法对整个堆内存中的对象进行标记。
  2. 由于老年代中的对象通常比较多,标记过程可能会比较耗时。

(二)整理阶段

  1. 对于老年代中的垃圾对象,进行清理。
  2. 可能会对存活的对象进行整理,以减少内存碎片。整理的方式可以是移动存活的对象,使它们连续存储。

(三)Metaspace 的清理(如果需要)

  1. 如果 Metaspace 空间不足,也可能触发 Full GC,此时会对 Metaspace 中的无用类信息等进行清理。

六、GC 触发条件的详细分析

(一)堆内存使用情况

  1. 新生代空间不足
    • 当 Eden 区和 Survivor 区中的对象占用空间超过一定比例时,触发 Minor GC。
    • 可以通过调整 JVM 参数来控制新生代的大小和比例,如 -Xmn 用于设置新生代的大小。
  2. 老年代空间不足
    • 当老年代中的对象占用空间超过一定比例时,触发 Major GC 或 Full GC。
    • 可以通过调整 JVM 参数来控制老年代的大小,如 -Xms 和 -Xmx 用于设置堆内存的初始大小和最大大小。

(二)对象的生命周期

  1. 对象的年龄增长
    • 对象在新生代中经过一次 Minor GC 后仍然存活,它的年龄会增加。当对象的年龄达到一定阈值时,会被晋升到老年代。
    • 可以通过调整 JVM 参数 -XX:MaxTenuringThreshold 来控制对象晋升到老年代的年龄阈值。
  2. 大对象直接进入老年代
    • 如果创建的对象占用空间较大,可能会直接进入老年代。可以通过调整 JVM 参数 -XX:PretenureSizeThreshold 来控制大对象的大小阈值。

(三)其他触发因素

  1. System.gc () 的调用
    • 在代码中显式调用 System.gc () 可能会触发 Full GC,但不建议在生产环境中使用,因为它会影响应用程序的性能。
  2. JVM 自身的策略
    • JVM 可能会根据一些内部策略触发 GC,如为了避免内存溢出等情况。

七、GC 算法详解

(一)标记 - 清除算法(Mark-Sweep)

  1. 算法原理
    • 标记阶段:通过可达性分析标记出所有存活的对象。
    • 清除阶段:清理所有未被标记的对象,释放内存空间。
  2. 优缺点
    • 优点:实现简单。
    • 缺点:会产生内存碎片,可能导致后续分配大对象时需要进行额外的整理操作。

(二)标记 - 整理算法(Mark-Compact)

  1. 算法原理
    • 标记阶段:与标记 - 清除算法相同。
    • 整理阶段:将所有存活的对象移动到一端,然后清理另一端的垃圾对象,从而避免内存碎片的产生。
  2. 优缺点
    • 优点:不会产生内存碎片。
    • 缺点:整理过程比较耗时,可能会影响应用程序的性能。

(三)复制算法(Copying)

  1. 算法原理
    • 将内存分为两块相等的区域,如新生代中的 Eden 区和 Survivor 区。当进行垃圾回收时,将存活的对象复制到另一块区域,然后清理原来的区域。
  2. 优缺点
    • 优点:实现简单,不会产生内存碎片。
    • 缺点:需要双倍的内存空间,当对象存活率较高时,复制操作会比较耗时。

八、实际案例分析

(一)案例背景

假设有一个 Java 应用程序,在运行过程中出现了频繁的 Full GC,导致应用程序性能下降。

(二)问题分析

  1. 通过监控工具(如 JVisualVM、jstat 等)观察堆内存的使用情况,发现老年代空间不足是触发 Full GC 的主要原因。
  2. 进一步分析发现,应用程序中存在一些大对象的创建,这些大对象直接进入老年代,导致老年代空间快速增长。
  3. 同时,应用程序中的某些对象的生命周期较长,经过多次 Minor GC 后仍然存活,最终晋升到老年代,也加剧了老年代空间的压力。

(三)解决方案

  1. 调整 JVM 参数
    • 增大堆内存的大小,如 -Xms 和 -Xmx,可以缓解老年代空间不足的问题,但要注意不要设置得过大,以免导致系统资源浪费。
    • 调整新生代和老年代的比例,如 -XX:NewRatio,可以适当增大新生代的空间,减少对象晋升到老年代的频率。
    • 调整对象晋升到老年代的年龄阈值,如 -XX:MaxTenuringThreshold,可以根据应用程序的实际情况适当降低年龄阈值,避免对象过早晋升到老年代。
  2. 优化对象创建
    • 避免创建不必要的大对象,如果确实需要创建大对象,可以考虑采用分块处理的方式,减少大对象对老年代的压力。
  3. 对象生命周期管理
    • 对于生命周期较长的对象,可以考虑采用对象池等技术,避免频繁地创建和销毁对象,减少对象晋升到老年代的机会。

九、GC 优化策略

(一)合理设置 JVM 参数

  1. 根据应用程序的特点和需求,合理设置堆内存的大小、新生代和老年代的比例、对象晋升年龄阈值等参数。
  2. 可以通过压力测试和性能监控来调整 JVM 参数,找到最适合应用程序的参数组合。

(二)对象生命周期管理

  1. 尽量减少不必要的对象创建,避免创建大量短期存活的对象,减少 Minor GC 的频率。
  2. 对于生命周期较长的对象,可以采用对象池等技术进行管理,提高对象的复用率。

(三)避免内存泄漏

  1. 及时释放不再使用的对象引用,避免内存泄漏。
  2. 对资源的使用(如数据库连接、文件流等)要及时关闭,防止资源泄漏导致内存占用过高。

(四)选择合适的 GC 算法

  1. 根据应用程序的特点选择合适的 GC 算法。例如,如果应用程序对响应时间要求较高,可以选择并发收集器;如果应用程序对吞吐量要求较高,可以选择并行收集器。
  2. 在 Java 8 及以上版本中,可以使用 G1 收集器,它在兼顾吞吐量和响应时间方面表现较好。

十、总结

JVM 中的垃圾回收是一个复杂而重要的过程。了解一次完整的 GC 流程对于优化 Java 应用程序的性能至关重要。通过合理设置 JVM 参数、管理对象生命周期、避免内存泄漏以及选择合适的 GC 算法,可以有效地减少 GC 的频率和时间,提高应用程序的性能和稳定性。

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

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

相关文章

密码学的基本原理

密码学是研究编制密码和破译密码的技术科学,以实现信息的保密性、完整性、可用性及抗抵赖性。以下是基本的密码学原理的详细解释: 一、密码学的基本概念明文:原始的、未加密的信息。密文:经过加密处理后的信息,只有持有…

illustrator(AI)-去掉文字有底色

AI去除文字底色教程: 1、打开AI软件,点击“文件”>“新建”,新建一个空白文档; 2、接着将其它版本的AI文件导入当前软件中,此时可以发现文字是有底色的,接下来小编教大家去除文字底色的方法&#xff1…

[OpenGL]使用OpenGL实现硬阴影效果

一、简介 本文介绍了如何使用OpenGL实现硬阴影效果,并在最后给出了全部的代码。本文基于[OpenGL]渲染Shadow Map,实现硬阴影的流程如下: 首先,以光源为视角,渲染场景的深度图,将light space中的深度图存储…

微服务是什么 SpringCloud是什么

微服务是什么 SpringCloud是什么 一、微服务概述二、微服务架构三、单体架构四、分布式架构五、SpringCloud概述六、SpringBoot和 SpringCloud的区别与联系七、SpringCloud版本选择 一、微服务概述 微服务(MicroServices)最初是由 Martin Fowler 于 2014…

K8S node节点没有相应的pod镜像运行故障处理办法

查看从节点状态 kubectl describe node k8s-node1以下是报错提示 解决办法 需要处理node1节点上的磁盘空间,磁盘空间需要在85%内 处理后的状态 处理正常

CSS教程(四)- 字体

1、尺寸单位 px 像素单位% 百分比,参照父元素对应属性的值进行计算em 字体尺寸单位,参照父元素的字体大小计算,1em16pxrem字体尺寸单位,参照根元素的字体大小计算,1rem16px 2、字体属性 介绍 CSS Fonts (字体)属性用于定义字体…

Redisson分布式锁全解析

Redisson分布式锁全解析:从基础到红锁,锁定高并发解决方案_redisson 红锁-CSDN博客

使用storcli工具配置RAID,收藏这一篇就够了

正文共:1888 字 28 图,预估阅读时间:2 分钟 前文(怎么把银河麒麟系统装进U盘?)提到,因为国产服务器固件暂不支持直接配置RAID,所以需要使用storcli这个工具来操作。今天就从RAID配置…

Three.js 搭建3D隧道监测

Three.js 搭建3D隧道监测 Three.js 基础元素场景scene相机carema网络模型Mesh光源light渲染器renderer控制器controls 实现3d隧道监测基础实现道路实现隧道实现多个摄像头点击模型进行属性操作实现点击模型发光效果 性能监视器stats引入使用 总结完整代码 我们将通过three.js技…

漫谈分布式唯一ID

文章目录 本系列前言UUIDDB自增主键Redis incr命令号段模式雪花算法 本系列 漫谈分布式唯一ID(本文)分布式唯一ID生成(二):leaf分布式唯一ID生成(三):uid-generator分布式唯一ID生成…

CSS教程(七)- 背景

介绍 背景属性可以设置背景颜色、背景图片、背景平铺、背景图片位置、背景图像固定等。 1 背景颜色 属性名:background-color 作用:指定HTML元素的背景色。 取值:英文颜色、16进制、rgb、rgba、transparent(一般为透明&#…

如何使用ffmpeg命令行进行录屏

录屏软件,我们去网上下载,发现有很多软件都是要收费的!但是录屏功能很难做吗?为啥都需要收费呢? 于是我整了个小demo,用于实现基础的屏幕录制功能。 思路很简单,考虑到 FFMpeg.exe是一个非常成…

Spring Boot 的核心原理和工作机制

1. 自动配置 (Auto-configuration) Spring Boot 的自动配置机制是它最引人注目的特性之一。它基于 Spring 的条件化配置(Conditional Configuration),允许 Spring Boot 根据类路径上的依赖和 Bean 的存在来决定如何配置应用。以下是自动配置的…

Python实现批量下载邮箱中京东商城的发票

以QQ邮箱为例 可以设置获取指定时间内的邮件中的发票 可以设置获取指定地点内的邮件中的发票 import poplib from urllib import request from email import policy from email.parser import BytesParser from email.header import decode_header, make_header from email.he…

SpringBoot中的注解详解(一)

一、RestController、RequestMapping 1、RestController注解 RestController注解是SpringMVC中的一个组合注解,用于标记一个类为控制器类。它实际上是Controller和ResponseBody注解的组合,表示该类中的所有方法都会返回JSON、XML等格式的数据&#xff…

网络安全SQL初步注入2

六.报错注入 mysql函数 updatexml(1,xpath语法,0) xpath语法常用concat拼接 例如: concat(07e,(查询语句),07e) select table_name from information_schema.tables limit 0,1 七.宽字节注入(如果后台数据库的编码为GBK) url编码:为了防止提交的数据和url中的一些有特殊意…

ES8安装配置kibana

Kibana安装及配置教程(包含密码重置步骤) 前提条件 确保系统中已安装Elasticsearch,版本应与Kibana一致(例如 8.15.2)。拥有sudo权限。 1. 添加Elastic GPG密钥 首先,添加Elastic的GPG密钥,确…

three.js 杂记

在Three.js中,Object3D是所有3D对象的基类,而Group是Object3D的一个子类。Group的目的是为了简化处理多个对象的集合。当你将对象添加到Group中时,它们会以一个单元格的形式被处理,参与Group的某些操作,例如位置更新、…

一文熟悉新版llama.cpp使用并本地部署LLAMA

0. 简介 最近是快到双十一了再给大家上点干货。去年我们写了一个大模型的系列,经过一年,大模型的发展已经日新月异。这一次我们来看一下使用llama.cpp这个项目,其主要解决的是推理过程中的性能问题。主要有两点优化: llama.cpp …

基于 Python flask 的微博舆论情感分析,微博大数据可视化系统

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…