简单谈谈Java中的垃圾回收器

本文深入解析了垃圾回收器的各种算法,包括标记-清除、复制、标记-压缩和分代算法,对比了它们的性能特点,如时间开销、空间浪费、内存碎片化及对象移动情况。同时介绍了常见的垃圾回收器,如串行、并行和并发标记扫描垃圾回收器,以及它们在新生代和老年代的应用。最后,给出了垃圾回收器的选择策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 垃圾回收器算法

目前主流垃圾回收器都采用的是可达性分析算法来判断对象是否已经存活,不使用引用计数算法判断对象存活的原因在于该算法很难解决相互引用的问题。

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

标记-清除算法由标记阶段和清除阶段构成。标记阶段是把所有活着的对象都做上标记的阶段;清除阶段是把那些没有标记的对象,也就是非活动对象回收的阶段。通过这两个阶段,就可以令不能利用的内存空间重新得到利用。

从标记-清除算法我们可以看出,该算法不涉及对象移动,但是可能会产生内存碎片化问题。空间碎片太高可能会导致程序运行时需要分配较大内存时候,无法找到足够的连续内存,需要其他垃圾回收帮助回收内存。

1.2 复制算法(Copying)

复制算法内存空间分为两块区域:From、to,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。

上面那种复制算法有一半的空间是浪费的。所以在Java新生代把内存区域分为Eden空间、from、to空间3个部分,from和to空间也称为survivor 空间,用于存放未被回收的对象。对象开始都是Eden生成;当回收时,将Eden和from中存活的对象移动到to区域中。

复制算法存在空间浪费的情况,始终都要保持一个Survivor是空闲的,并且在GC的时候要是存活对象大小超过了Survivor中的大小,就需要另外的策略存储存活对象。

目前open JDK新生代回收策略就是采用的复制算法,其中Eden和Survivor的默认配置为8:1

1.3 标记-压缩算法(Mark-Compact)

标记-压缩算法由标记阶段和压缩阶段构成。标记阶段标记-清除算法中的标记阶段完全一样,压缩阶段是让所有存活的对象向一端移动。这样空闲内存都在另外一端,属于连续空间,不存在内存碎片化问题,但是会产生对象移动。

1.4 分代算法(Generational GC)

根据对象的不同生命周期分别管理, JVM 中将对象分为我们熟悉的新生代、老年代和永久代分别管理。这样做的好处就是可以根据不同类型对象进行不同策略的管理,例如新生代中对象更新速度快,就会使用效率较高的复制算法。老年代中内存空间相对分配较大,而且时效性不如新生代强,就会常常使用Mark-Sweep-Compact (标记-清除-压缩)算法。

2. 各个算法的性能比较

标记-清除复制算法标记压缩
时间开销中等最快最慢
空间浪费不存在存在不存在
内存碎片化存在不存在不存在
是否移动对象

从表格中我们可以看出,复制算法效率最高,也不存在内存碎片化,但有空间浪费的现象,一般用来处理新生代中的对象
而标记-清除算法和标记压缩算法则主要处理老年代中对象内存分配比较大的,并且时效性不如新生代的

3. 常见的垃圾回收器

3.1 按照处理过程分类:

  • 串行垃圾回收器(Serial Garbage Collector)
  • 并行垃圾回收器(Parallel Garbage Collector)
  • 并发标记扫描垃圾回收器(CMS Garbage Collector)
3.1.1 串行垃圾回收器
回收器名称使用的算法作用区域单/多线程备注
Serial复制算法新生代单线程简单高效,不建议使用,Client默认的
Serial Old标记-压缩老年代单线程能和所有的Young GC搭配使用
3.1.2 并行垃圾回收器
回收器名称使用的算法作用区域单多线程备注
ParNew复制算法新生代多线程唯一和CMS搭配的新生代垃圾回收器
Parallel Scavenge复制算法新生代多线程更关注于吞吐量
ParNew Old标记整理老年代多线程搭配Parallel Scavenge垃圾回收器
3.1.3 并发标记扫描垃圾回收器
回收器名称使用的算法作用区域单多线程备注
CMS标记-清除老年代多线程追求最短的暂时时间

3.2 按照处理的区域分类:

3.2.1 新生代(Young GC)
  • Serial
  • ParNew
  • Parallel Scavenge
3.2.2 老年代(Old GC)
  • Serial Old
  • ParNew Old
  • CMS

4. 垃圾回收器的选择策略

  1. 客户端程序:Serial + Serial Old;
  2. 吞吐率优先的服务端程序(比如:计算密集型):Parallel Scavenge + Parallel Old;
  3. 响应时间优先的服务端程序:ParNew + CMS。

目前很大一部分的Java应用都集中在互联网的服务器端,这类应用尤其关系服务的响应时间,希望应用暂停时间更短,所以基本上使用的都是 ParNew + CMS

在启动JVM参数加上 -XX:+UseConcMarkSweepGC 这个参数表示对于老年代的回收采用 CMS。

4.1 CMS执行过程

CMS 的回收过程主要分为下面的几个步骤:

  1. 初始标记(Initial Mark)
  2. 并发标记(Concurrent marking)
  3. 并发预清理(Concurrent pre-preclean)
  4. 重新标记(Final Remark)
  5. 并发清理(Concurrent sweep)
  6. 并发重置(Concurrent reset)
Java垃圾回收机制是Java虚拟机(JVM)自动管理内存的一种机制。在Java程序中,程序员不需要手动分配和释放内存,而是由JVM负责自动回收不再使用的对象。 Java垃圾回收机制的基本原理是通过“可达性分析”来确定哪些对象是存活的,然后回收那些不再被引用的对象。可达性分析是从一组称为“GC Roots”的起始对象开始,通过引用链来遍历和标记可达对象。无法通过引用链访问到的对象则被标记为可回收的垃圾对象。 Java垃圾回收机制的工作过程分为三个阶段:标记、清除和整理。在标记阶段,垃圾回收器标记所有可达对象。在清除阶段,垃圾回收器回收标记为垃圾的对象,并且释放它们所占用的内存空间。在整理阶段,垃圾回收器将存活的对象向一端移动,以便在内存布局中形成连续的内存块。这样可以解决内存碎片问题,提高内存利用率。 Java垃圾回收机制具有以下优点: 1. 自动化管理:程序员不需要手动释放内存,减少了内存泄漏和悬挂对象(对象已被释放但仍然被引用)的可能性。 2. 高效性能:Java垃圾回收机制采用分代回收算法,根据对象的存活时间将内存分为多个代,根据不同的代使用不同的垃圾回收算法,提高了回收效率。 3. 防止空指针异常:垃圾回收器可以检测并处理不再使用的对象,避免了程序运行过程中的空指针异常错误。 4. 提高开发效率:不需要手动管理内存,减少了程序员的工作量,提高了开发效率。 然而,Java垃圾回收机制也存在一些问题,比如垃圾回收过程可能会导致程序的暂停时间变长,对实时性要求较高的程序可能会受到影响。此外,垃圾回收机制的性能也会受到堆内存的大小和程序的运行负载等因素的影响。 总的来说,Java垃圾回收机制是一种非常成熟和高效的内存管理机制,它解放了程序员手动管理内存的负担,提高了开发效率和程序的健壮性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值