JVM垃圾回收详解

一、基本概念

1、HotSpot VM :是由 Oracle 公司开发的一种 Java 虚拟机(JVM),是 Java SE 平台上最广泛使用的虚拟机之一。它是 OpenJDK 的一部分,也是 Oracle JDK 的基础之一。使用即时编译(Just-In-Time Compilation,JIT)技术来提高 Java 程序的性能。

2、GC 堆:Java 堆是垃圾收集器管理的主要区域,因此也被称作GC 堆 (Garbage Collected Heap)。

3、HotSpot VM 的GC 实现分类只有两大种:

部分收集 (Partial GC):

  1. 新生代收集(Minor GC / Young GC):只对新生代进行垃圾收集;
  2. 老年代收集(Major GC / Old GC):只对老年代进行垃圾收集。需要注意的是 Major GC 在有的语境中也用于指代整堆收集;
  3. 混合收集(Mixed GC):对整个新生代和部分老年代进行垃圾收集。

整堆收集 (Full GC):收集整个 Java 堆和方法区。

二、堆空间的基本结构:

Eden 区、两个 Survivor 区 S0 和 S1 都属于新生代,中间一层属于老年代,最下面一层属于永久代(jdk 1.8之后用元空间代替)。
在这里插入图片描述

1、新生代(Young Generation):

新生代是堆空间的一部分,主要用于存放新创建的对象。它通常被划分为 Eden 区、两个 Survivor 区(通常是 From 和 To 区)。
新创建的对象首先会被分配到 Eden 区。当 Eden 区空间不足时,触发一次 Minor GC(年轻代垃圾收集),Eden 区中存活的对象将被移动到其中一个 Survivor 区。经过多次 Minor GC 后,仍然存活的对象会被移动到另一个 Survivor 区,同时清空该 Survivor 区。这个过程称为对象的年龄晋升。
对象在 Survivor 中每熬过一次 MinorGC,年龄就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。

2、老年代(Old Generation):

老年代用于存放经过多次年龄晋升后仍然存活的对象,以及大对象。在一些垃圾收集器中,还可能包含部分永久代(PermGen)或元空间(Metaspace)的数据。
当老年代空间不足时,会触发一次 Full GC(完全垃圾收集),对整个堆空间进行垃圾收集。Full GC 会清理所有的新生代和老年代,以及执行一些额外的清理工作,如永久代的垃圾回收和类卸载。

3、永久代(PermGen)或元空间(Metaspace):

在较早的Java版本中,永久代用于存储类的元数据信息、常量池等。但从Java 8 开始,永久代被移除,取而代之的是元空间(Metaspace)。
元空间用于存储类的元数据信息,包括类的结构、方法、字段等。它位于堆空间之外,通常由操作系统的本机内存管理。

三、内存分配和回收原则

  1. 对象优先在 Eden 区分配:当一个对象被创建时,它首先会被分配到 Eden 区域,这是新生代中的一部分。大部分对象的生命周期都很短暂,它们很快就会成为垃圾,因此将它们分配到 Eden 区是合理的。

  2. 大对象直接进入老年代:对于大对象(大于一定大小阈值,字符串、数组、集合对象等),JVM 可能会选择直接将它们分配到老年代,而不是分配到新生代的 Eden 区。这样做的原因是,大对象往往生命周期较长,如果将它们分配到新生代,可能会导致频繁的 Minor GC,影响程序的性能。

  3. 长期存活的对象将进入老年代:当对象经过多次垃圾收集仍然存活下来时,它们可能会被认为是长期存活的对象。这些对象会逐渐从新生代的 Survivor 区域晋升到老年代。这是因为老年代相对于新生代来说,拥有更大的内存空间,更适合长期存活的对象。

四、判断对象是否死亡的方法

垃圾收集器主要负责回收堆内存中的对象,对堆垃圾回收前的第一步就是要判断哪些对象已经死亡。

1、引用计数法

给对象中添加一个引用计数器:
每当有一个地方引用它,计数器就加 1;
当引用失效,计数器就减 1;
任何时候计数器为 0 的对象就是不可能再被使用的。
缺点:很难解决对象之间循环引用的问题。

2、可达性分析算法

算法的基本思想是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。
根对象通常包括以下几种类型:
栈中的引用:包括本地变量表中的引用、方法参数等。
静态变量引用:即类变量,存在于类的静态区域。
JNI(Java Native Interface)引用:通过JNI接口创建的引用。
一旦确定了哪些对象是活动对象,垃圾收集器就可以清理未被标记的对象,释放它们占用的内存空间。通常,被回收的对象将被归入不同的代(新生代或老年代),并根据对象的存活时间来选择不同的垃圾收集算法进行处理。

五、垃圾收集算法

  1. 标记-清除算法(Mark-Sweep Algorithm):这是最基本的垃圾收集算法之一。它分为两个阶段:标记阶段和清除阶段。在标记阶段,从根对象开始,通过可达性分析标记所有活动对象。在清除阶段,遍历整个堆内存,清除未标记的对象,即垃圾对象。标记-清除算法的缺点是会产生内存碎片,可能导致堆内存的不连续性,影响内存分配效率。

  2. 复制算法(Copying Algorithm):复制算法将堆内存分为两个大小相等的区域,一部分称为活动区,用于存放存活的对象;另一部分称为闲置区,用于垃圾收集。当活动区的内存空间耗尽时,将活动区中存活的对象复制到闲置区,同时清除活动区中的所有对象。这样可以避免内存碎片的产生,但需要额外的空间用于存放复制后的对象。

  3. 标记-整理算法(Mark-Compact Algorithm):标记-整理算法结合了标记-清除算法和复制算法的优点。首先,通过可达性分析标记所有活动对象;然后,将活动对象紧凑地移动到堆内存的一端,清除堆内存另一端的所有未标记对象,即垃圾对象。标记-整理算法可以避免内存碎片的产生,并且不需要额外的内存空间,但可能会产生对象移动的额外开销。

一般来说新生代采用标记-复制算法,老年代采用标记-整理或者标记-清除算法。

六、垃圾收集器

JDK 默认垃圾收集器(使用 java -XX:+PrintCommandLineFlags -version 命令查看)。

1、JDK 8:Parallel Scavenge(新生代)+ Parallel Old(老年代)

Parallel Scavenge 是一种面向新生代的垃圾收集器,它使用复制算法来实现快速的垃圾收集。它会尽可能地利用多线程来加速垃圾收集过程,适用于需要高吞吐量的场景。
Parallel Old 是一种面向老年代的垃圾收集器,同样采用并行方式进行垃圾收集。它适用于大型应用程序,能够在较短时间内完成全局垃圾收集。

2、JDK 9 ~ JDK20: G1

G1 是 JDK 9 及以后版本中引入的全新垃圾收集器,它是一种面向整个堆内存的并发、分代、照顾吞吐量的垃圾收集器。相比于传统的 CMS(Concurrent Mark-Sweep)收集器,G1 在堆内存管理和回收方面提供了更好的性能和可预测性。
G1 垃圾收集器在处理大堆内存时表现出色,它通过将堆内存划分为多个大小相等的区域(Region),并根据垃圾收集的需要来动态调整区域的大小和分配策略,从而有效地避免了堆内存碎片化问题,并且能够在保证较低停顿时间的同时实现高吞吐量的垃圾收集。G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region(这也就是它的名字 Garbage-First 的由来) 。

3、Serial 收集器

Serial(串行)收集器是最基本、历史最悠久的垃圾收集器了。大家看名字就知道这个收集器是一个单线程收集器了。它的 “单线程” 的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程( “Stop The World” ),直到它收集结束。

4、ParNew 收集器

ParNew 收集器其实就是 Serial 收集器的多线程版本,它是许多运行在 Server 模式下的虚拟机的首要选择,除了 Serial 收集器外,只有它能与 CMS 收集器配合工作。

其他的就不一一介绍了。

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

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

相关文章

为什么使用bean注解创建IRule,就可以定义负载均衡规则

Bean public IRule randomRule() { return new RandomRule(); } 当您在Spring Cloud中定义一个IRule的Bean时,您实际上是在配置Ribbon的负载均衡规则。这个Bean会被注入到Ribbon客户端中,并在客户端发起服务调用时用于决定如何选择目标服务实例。 这里需…

ELK原理详解

ELK原理详解 一、引言 在当今日益增长的数据量和复杂的系统环境中,日志数据的收集、存储、分析和可视化成为了企业运营和决策不可或缺的一部分。ELK(Elasticsearch、Logstash、Kibana)堆栈凭借其高效的性能、灵活的扩展性和强大的功能&…

使用ROW_NUMBER()分组遇到的坑

1、再一次清洗数据时,需要过滤重复数据,使用了ROW_NUMBER() 来分组给每组数据排序号 在获取每组的第一行数据 with records as(select cc.F_Id as Id,REPLACE(cc.F_CNKITitle,char(10),1) as F_CNKITitle,REPLACE(REPLACE(cc.F_Special,专题&#xff1…

Kubernetes——基础认识

目录 前言 什么是云原生 云元素 K8s与中间件以及微服务之间的关系 Kubernetes发展历史 一、简介 1.Kubernetes是什么 2.为什么要使用Kubernetes 3.Kubernetes特性 3.1自我修复 3.2弹性伸缩 3.3自动部署和回滚 3.4服务发现和负载均衡 3.5集中化配置管理和密钥管理…

车载测试系列:车载测试流程

车载测试流程是保证软件质量的重要支撑,优秀的团队都必须拥有规范的流程体系支撑,它能够约束测试人员的测试行为,约束测试环境的测试精度,提升测试的覆盖度,保证团队成员工作的协调性。 该测试流程建立的依据&#xf…

书生浦语训练营第2期-第7节笔记

一、为什么要研究大模型的评测? 首先,研究评测对于我们全面了解大型语言模型的优势和限制至关重要。尽管许多研究表明大型语言模型在多个通用任务上已经达到或超越了人类水平,但仍然存在质疑,即这些模型的能力是否只是对训练数据的…

二分查找向下取整导致的死循环69. x 的平方根

二分查找向下取整导致的死循环 考虑伪题目:从数组arr中查找出目标元素target对应的下标,如果数组中不存在目标元素,找 到第一个元素值小于target的元素的下标。 编写二分查找算法如下: Testvoid testBinarySearch(){int[] arr n…

java如何打印数组所有元素

java如何打印数组所有元素 用for循环的话 语法格式是 for(int i0;i<数组名.length;i) { System.out.prontln(数组名[i]); } 如果用while循环 先定义一个变量&#xff0c;变量的值等于0 假定变量名为j int j0; while(j<数组名.length) { System.out.println(数组…

Web 功能以及源码讲解

Web 功能以及语言讲解 培训、环境、资料、考证 公众号&#xff1a;Geek极安云科 网络安全群&#xff1a;624032112 网络系统管理群&#xff1a;223627079 网络建设与运维群&#xff1a;870959784 移动应用开发群&#xff1a;548238632 短视频制作群&#xff1a; 744125867极…

leetcode203-Remove Linked List Elements

题目 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5] 示例 2&#xff1a; 输入&…

Eigen中的刚体变换表达

在Eigen中&#xff0c;旋转矩阵、变换矩阵、欧拉角都可以表示为Eigen库中的特定类型的矩阵。 1、旋转矩阵 旋转矩阵通常用于表示三维空间中的旋转操作。在Eigen中&#xff0c;可以使用Eigen::Matrix3d类型来表示三维的旋转矩阵。通常&#xff0c;旋转矩阵是一个正交矩阵&…

网络新手如何上手水牛社软件?我的建议与看法

水牛社是一款专为电脑用户设计的软件&#xff0c;拥有明确的著作权&#xff0c;其核心功能在于发布和整合各类网络活动任务资源、教程等&#xff0c;内容多元且不设固定分类。其靠谱程度取决于你对软件的了解程度和个人需求的适配性。 软件内部包含五个主要栏目&#xff0c;大…

轮廓提取、矩形标记时,点的位置需要重标

在下图中的0&#xff0c;3&#xff0c;1&#xff0c;2位置如何变换成0&#xff0c;1&#xff0c;2&#xff0c;3 先显示结果&#xff1a; 变换之后图&#xff1a; 这边提供两种解决方案&#xff1a; 第一种&#xff1a;将坐标值相加求和&#xff0c;采用冒泡排序实现从小到大…

使用固定公网地址远程访问开源服务器运维管理面板1Panel管理界面

文章目录 前言1. Linux 安装1Panel2. 安装cpolar内网穿透3. 配置1Panel公网访问地址4. 公网远程访问1Panel管理界面5. 固定1Panel公网地址 前言 1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。高效管理,通过 Web 端轻松管理 Linux 服务器&#xff0c;包括主机监控、…

顺序表的实现(迈入数据结构的大门)(完整代码)

seqlist.h #pragma once typedef int SLDataType;#include<stdio.h> #include<stdlib.h> #include<assert.h>typedef struct SeqList {SLDataType* a;int size; // 有效数据个数int capacity; // 空间容量 }SL;//初始化和销毁 void SLInit(SL* ps); void SL…

ReentrantLock的原理

总结&#xff1a; ReentrantLock 的基本实现可以概括为&#xff1a;先通过 CAS 尝试获取锁。如果此时已经有线程占据了锁&#xff0c;那就加入 AQS 队列并且被挂起。当锁被释放之后&#xff0c;排在 CLH 队列队首的线程会被唤醒&#xff0c;然后 CAS 再次尝试获取锁。在这个时候…

reqwest - Rust HTTP Client

文章目录 关于 reqwest使用添加依赖Get 请求Post 请求FormsJSON 关于 reqwest An easy and powerful Rust HTTP Client github : https://github.com/seanmonstar/reqwest文档&#xff1a;https://docs.rs/reqwest/latest/reqwest/ 特点 Async and blocking ClientsPlain bo…

c语言循环题目

c语言循环题目 已知sinx的近似计算公式如下sin xx- x3/3! x’/5!-x7/7!.(-1)n-1x2n-1/(2n-1)!其中x为弧度&#xff0c;n为正整数。编写程序根据用户输入的x和n的值&#xff0c;利用上述近似计算公式计算sinx的近似值&#xff0c;要求输出结果小数点后保留8位 int main() {in…

Excel文件解析---超大Excel文件读写

1.使用POI写入 当我们想在Excel文件中写入100w条数据时&#xff0c;使用XSSFWorkbook进行写入时会发现&#xff0c;只有将100w条数据全部加载到内存后才会用write()方法统一写入&#xff0c;效率很低&#xff0c;所以我们引入了SXXFWorkbook进行超大Excel文件读写。 通过设置 …

C语言leetcode刷题笔记1

C语言leetcode刷题笔记1 第1题&#xff1a;136.只出现一次的数字两次遍历&#xff08;O(numsSize^2)&#xff09;位运算 第2题&#xff1a;202.快乐数快慢指针记录历史数据 第3题&#xff1a;53.最大子数组和暴力求解&#xff08;超时&#xff09;动态规划分治 第1题&#xff1…