【JVM】详解Java中的垃圾回收

ops/2024/10/20 20:50:26/

概念

在传统的编程语言中,内存的管理通常需要由开发人员手动进行分配和释放。这种手动管理容易导致一系列问题,例如内存溢出(内存不足时继续申请内存)和内存泄漏(已不再使用的内存未被释放)。这些问题可能导致程序的不稳定,甚至崩溃。

然而,Java通过引入垃圾回收(Garbage Collection,简称GC)机制,实现了自动内存管理。垃圾回收机制主要针对堆内存中的对象进行管理,其核心功能是自动分配和回收不再使用的对象的内存。

通过GC,Java虚拟机(JVM)在程序运行时可以自动识别并清理那些不再使用的对象,从而使开发人员无需手动管理内存。这不仅提高了开发效率,减少了内存管理相关的错误,还增强了程序的可靠性和稳定性。

垃圾判断算法

要判断对象是否为垃圾,通常有两种主流算法:引用计数法和可达性分析算法。

1.引用计数法

引用计数法为对象添加一个引用计数器,每当有一个地方引用该对象时,计数器加1;当引用失效时,计数器减1。计数器为0的对象即被认为是垃圾,可以被回收。这种方法简单直观,但无法处理循环引用的问题,即两个对象互相引用,但不再被其他对象引用的情况。

2.可达性分析算法

可达性分析算法通过一系列称为“GC Roots”的根对象开始,沿着这些对象的引用链进行遍历。凡是能被GC Roots引用到的对象都是“可达”的,不会被回收;而那些不可达的对象则被认为是垃圾,将被清理。这种算法解决了引用计数法的循环引用问题,是Java中垃圾回收的主要算法。

垃圾收集算法

垃圾收集算法是具体执行垃圾回收任务的方法,常见的算法包括:

1. 标记-清除算法

原理与步骤:
标记-清除(Mark-Sweep)算法是垃圾回收中最基础的算法之一。它的工作原理分为两个阶段:

  • 标记阶段:从根集合(GC Roots)出发,遍历堆中的所有对象,标记所有仍在使用中的对象。
  • 清除阶段:遍历堆中的所有对象,清除那些未被标记的对象,即认为它们是垃圾对象,可以回收其占用的内存。

优点:

  • 简单直观:标记-清除算法实现简单,只需对对象进行标记和清除操作,无需考虑对象的复制或移动。
  • 无须移动对象:该算法不移动对象的位置,因此适用于堆中有大量存活对象的情况。

缺点:

  • 产生内存碎片:由于未被标记的对象直接被清除,剩余的存活对象可能分散在堆的各个位置,导致内存碎片的产生。这些碎片可能导致大对象无法分配足够的连续内存空间。
  • 效率不高:标记和清除阶段都需要遍历整个堆,效率较低,尤其是在对象较多的情况下。

2. 复制算法

原理与步骤:
复制算法(Copying)将堆内存分为两个相同大小的区域——通常称为“From空间”和“To空间”:

  • 分配内存:所有新创建的对象最初分配在“From空间”。
  • GC操作:当“From空间”用尽时,垃圾收集器将仍在使用的对象复制到“To空间”,然后清空“From空间”。接下来,原来的“To空间”成为新的“From空间”,进行下一轮分配和回收。

优点:

  • 避免内存碎片:通过将存活对象复制到另一块连续的空间,复制算法可以有效避免内存碎片问题。
  • 速度快:由于每次只处理存活对象,且无需在堆中遍历所有对象,复制算法在回收大量短命对象时效率很高。

缺点:

  • 内存利用率低:由于内存分为两个区域,每次只使用其中一个区域,这意味着实际内存利用率只有50%。
  • 不适合老年代:在老年代中,存活对象较多,复制操作成本较高,内存浪费严重。因此,复制算法主要用于新生代回收。

3. 标记-整理算法

原理与步骤:
标记-整理(Mark-Compact)算法结合了标记-清除和复制算法的优点:

  • 标记阶段:与标记-清除算法相同,从根集合出发标记存活对象。
  • 整理阶段:不同于标记-清除算法,标记-整理算法在清除阶段并不直接清理未标记的对象,而是将所有存活的对象向堆的一端移动,按顺序排列。最后,清理掉边界外的所有内存。

优点:

  • 避免内存碎片:通过将对象整理到堆的一端,标记-整理算法有效避免了内存碎片问题,使得堆内存更加紧凑。
  • 内存利用率高:标记-整理算法避免了复制算法的内存浪费问题,最大限度地利用可用内存。

缺点:

  • 效率较低:虽然避免了碎片问题,但移动对象的操作增加了算法的开销,尤其在堆中对象较多的情况下,标记和整理阶段都需要较长时间。

4. 分代回收算法

原理与背景:
分代回收(Generational Collection)算法基于“对象生命周期假说”,即大部分对象在内存中的生命周期是很短的,而少部分对象则会长期存活。因此,JVM将堆内存划分为几代:新生代(Young Generation)、老年代(Old Generation)和永久代(在Java 8后被元空间取代)。

  • 新生代:大多数对象在新生代分配。当新生代的内存耗尽时,会触发“Minor GC”,通常使用复制算法来回收内存。由于新生代中的对象大多数都是短命的,因此复制算法的效率很高。
  • 老年代:存活时间较长的对象会从新生代晋升到老年代。老年代的GC称为“Major GC”或“Full GC”,通常使用标记-整理算法,因为老年代中存活对象较多,适合这种算法。
  • 永久代/元空间:永久代存放类的元数据,Java 8之后被移到元空间(Metaspace),不再由堆管理。

优点:

  • 适应对象生命周期:分代回收算法根据对象的生命周期特点优化了内存管理,避免了一刀切的回收策略。
  • 高效回收:新生代使用复制算法快速回收短命对象,老年代使用标记-整理算法处理长命对象,提高了整体GC的效率。

缺点:

  • 复杂性高:分代回收的策略需要对对象的生命周期进行判断和管理,增加了垃圾收集的复杂性。
  • 老年代GC成本高:虽然新生代GC频繁且快速,但老年代GC较为昂贵,可能导致应用程序停顿较长时间。

垃圾收集器

垃圾收集器是垃圾收集算法的具体实现,JVM中常见的垃圾收集器包括:

1.Serial收集器(复制算法)

Serial收集器是新生代的单线程收集器,所有的垃圾收集工作都是由一个线程完成的。虽然简单且在单核CPU上效率较高,但在多核环境下性能有限。

2.ParNew收集器(复制算法)

ParNew收集器是Serial收集器的多线程版本,适用于多核CPU环境,在并行执行垃圾收集时比Serial收集器更高效。

3.Parallel Scavenge收集器(复制算法)

Parallel Scavenge收集器也是新生代的并行收集器,但其目标是最大化吞吐量,即最小化垃圾回收时间与应用程序运行时间的比例。它是JDK1.8 默认收集器 ,适用于对响应时间要求不高的后台任务。

4.Serial Old收集器(标记-整理算法)

Serial Old收集器是Serial收集器在老年代的实现,依旧是单线程,但适用于老年代对象的垃圾回收。

5.Parallel Old收集器(标记-整理算法)

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,同样追求吞吐量优先,适用于大规模并发任务。

6.CMS(Concurrent Mark Sweep)收集器(标记-清除算法)

CMS收集器的目标是最小化垃圾回收的停顿时间,它通过多线程并行执行标记和清除,适用于需要低停顿时间的应用,如交互式应用。

7.G1(Garbage First)收集器(标记-整理算法)

G1收集器是JDK 1.7引入的并行垃圾收集器,基于标记-整理算法,避免了内存碎片问题。G1的一个显著特点是,它可以回收整个Java堆(包括新生代和老年代),适用于需要处理大堆内存的应用程序。

通过合理选择垃圾收集器和算法,可以有效提升Java应用程序的性能,满足不同应用场景的需求。


http://www.ppmy.cn/ops/98094.html

相关文章

EXCEL——Vlookup17个高级用法

大纲 一、基本语法 1、参数详解 二、入门篇 1、单条件查找 2、屏蔽查找返回的错误值 三、进阶篇 1、反向查找 2、包含查找 3、区间查找 4、含通配符查找 5、多列查找 6、多区域查找 四、高级篇 1、多条件查找 2、合并单元格查找 3、带合并单元格的多条件查找 …

迁移学习代码复现

一、前言 说来可能令人难以置信,迁移学习技术在实践中是非常简单的,我们仅需要保留训练好的神经网络整体或者部分网络,再在使用迁移学习的情况下把保留的模型重新加载到内存中,就完成了迁移的过程。之后,我们就可以像训练普通神经网络那样训练迁移过来的神经网络了。 我们…

Go语言基础--嵌套循环(多重循环)

嵌套循环可以让你在一个循环内部再嵌套另一个或多个循环。这种结构在处理多维数据(如二维数组、矩阵)或需要遍历多个集合时非常有用。 for 初始化语句1; 条件判断1; 更新语句1 { // 外层循环体 for 初始化语句2; 条件判断2; 更新语句2 { // 内层循…

Clearpool 推出 Ozean:专注 RWA 的高性能创新区块链

引言 真实资产(Real-World Assets, RWA)指的是诸如房地产、债券、股票等在现实世界中存在并具有价值的资产。随着 DeFi 的发展,加密创新者们开始探索如何将传统金融市场中的资产引入区块链世界,以扩展 DeFi 的应用范围。然而&…

pgsql清理表的oids选项

1. 生成脚本 SELECTn.nspname as "Schema",c.relname as "Table",c.relhasoids as "With OIDs", ALTER TABLE || n.nspname ||. || c.relname || SET WITHOUT OIDS; FROMpg_class cJOIN pg_namespace n ON n.oid c.relnamespace WHEREc.relk…

机器人走路的问题

public class Test52 {//假设有N个位置,记为1-N,N大于或等于2//开始机器人在M位置上(M为1-N中的一个)//如果机器人来到1位置,那么下一步只能向右来到2位置//如果机器人来到N位置,那么下一步只能向左来到N-1…

Oracle数据库中实现分页

在Oracle数据库中实现分页通常有以下几种方法,每种方法都有其适用场景和优缺点。 1. 使用ROWNUM ROWNUM是Oracle为结果集的每一行分配的一个唯一的数字,这个数字表示行被检索出来的顺序。但是,需要注意的是,ROWNUM是在结果集产生…

C学习(数据结构)-->二叉树

目录 一、树 1、概念与结构 2、相关术语 3、树的表示 孩子兄弟表示法: ​编辑​编辑 二、二叉树 1、概念与结构 2、特殊的二叉树 1)满二叉树 2)完全二叉树 3、二叉树的存储结构 1)顺序存储 2)链式结构 一、树…