当前位置: 首页 > news >正文

JVM垃圾收集器相关面试题(1)

垃圾收集与内存管理摘要

一.核心垃圾收集算法对比

算法原理优点缺点适用场景
标记-清除两次遍历(标记存活对象→清除未标记对象)实现简单内存碎片化、双遍历效率低老年代(结合整理)
标记-复制内存对半分,存活对象复制到空白区无碎片、效率高内存利用率50%新生代
标记-整理标记后移动存活对象至内存端无碎片、内存利用率高对象移动开销大老年代

二.分代收集核心机制

内存分代结构

  • 新生代(1/3堆)

    • Eden区:80%空间,新对象初始分配区,通过连续内存分配优化短命对象处理
    • Survivor区:From/To各10%,存放至少存活一次的对象,采用复制算法
  • 老年代(2/3堆):存放长周期对象,采用标记-清除/整理算法

  • 元空间(本地内存):类元数据存储,GC条件更宽松

回收流程关键点

  1. Minor GC(新生代)

    • 触发条件:Eden区满
    • 对象晋升:年龄阈值(15)或Survivor空间不足
    • 复制效率:仅处理存活对象,存活率<10%时最优
  2. Full GC(全局)

    • 触发条件:老年代不足/显式调用
    • 性能影响:全堆扫描,停顿时间显著

三.永久代演进对比

特性Java7永久代Java8+元空间
存储位置堆内存本地内存
GC触发条件类+类加载器+反射引用全解除类加载器回收即释放
内存管理固定大小易OOM动态扩展
性能影响Full GC时扫描独立回收机制

四.对象存活判定

  • 可达性分析法:通过GC Roots(栈变量、静态属性、JNI引用)遍历引用链
  • 死亡判定流程:两次标记机制(可达性分析→finalize()自救机会→不可达回收)

关键设计理念:基于对象生命周期特征(98%对象朝生夕死)进行分代优化,通过空间换时间(复制算法)和延迟处理(老年代整理)平衡吞吐量与停顿时间。

正文

一.常见的垃圾收集算法

标记-清楚

原理:

  • 标记:从GC Roots(例如栈中的变量,静态变量等)开始便利,标记出所有被引用的对象

  • 清除:遍历整个堆,清除没被标记的对象。

缺点

  • 标记和清除各遍历一次,效率低下
  • 可能产生大量不连续的空间,当程序需要分配较大内存时,可能会因为无法找到连续的内存空间导致内存不足,从而提前触发垃圾回收。

标记-复制

原理:将内存空间化为等大的两块,一般称为(Form和To空间)。每次只是用其中的一块,当这一块用满后,就将存活的对象复制到另一块空间,然后把原来使用的空间直接清理掉。

优点:

  • 只需要复制存活的对象效率很高
  • 清除后的空间是连续的

缺点

  • 内存空间只占用一般,另一半用来存放复制后的内存,空间浪费严重

标记-整理

原理

  • 标记:与标记-清除算法相同,从GC Root开始遍历所有存活的对象
  • 整理:将存活对象向内存的一端移动,此过程不会清除垃圾对象,而是会把存活对象直接挪到垃圾对象,类似于赋值操作,然后直接清理掉边界以外的内存空间。

优点

  • 解决了标记-清除算法需要遍历两次和会产生内存碎片的问题,同时也不会和标记-整理算法一样浪费一半的空间

缺点

  • 整理过程需要移动对象,效率相对较低,尤其是对象过多的情况下

分代收集

原理

  • 基于对象存活周期不同,讲内存化为不同的代

  • 新生代:对象通常“朝生夕死”存活率低。一般采用标记-复制算法

  • 老生代:对象通常存活率高,占用空间大。一般采用标记-清除标记-整理算法

优点

  • 根据对象存活特点采用不同算法,提高垃圾回收效率,减少对应用程序性能的影响。

缺点

  • 需要对堆内存进行分代管理,增加了垃圾回收器的实现复杂度。

二.分代垃圾回收器工作原理详解

分代垃圾回收器是基于一个假说:大部分对象生命周期极短,少数对象长期存活。

堆内存分代结构

  • 新生代

    • 占堆内存的1/3

    • 分为Eden区(80%)和两个Survivor区(From+To 各10%)

      • 为什么要有Eden区?直接和正常的标记-复制算法一样不好吗?只要From和To两个分区
      • 因为大多数内存活不过一次GC,新对象直接在连续内存的 Eden 区分配,避免频繁内存整理。集中处理 “朝生暮死” 的对象,减少对 Survivor 区的频繁操作。
        如果只是用From和To两个Survivor区域,则无法隔离新对象和多次存活对象,导致每次 GC 需扫描全部区域,效率降低。
        所以我们再Survivor中存放的是至少存货过一次的对象,Eden区只存放新对象
    • 新对象优先在Eden区分配,若Eden区空间不足,则出发Minor GC

  • 老生代

    • 占堆内存2/3,存放长期存活的对象
    • 当老生代空间不足时,则出发Full GC或Major GC,回收整个堆
  • 元空间

    • 取代永久代,存放类元数据,常量池等,GC主要针对不在使用的类的加载器和常量池

对象分配与回收流程

1.对象分配

  • 新对象先分配到Eden区,若Eden区已满,则出发Minor GC

  • 大对象(如长数组)会直接进入老年代

2.Minor GC(新生代回收)

前面我们总体到Minor GC,那么他到底是个什么呢?

  • 存活对象:从 Eden 和 Survivor 区复制到另一个 Survivor 区(复制算法)。
  • 对象年龄:每熬过一次 Minor GC,年龄 + 1。
  • 晋升老年代:年龄达到阈值(默认 15)或 Survivor 区空间不足时晋升

3.Full GC(老年代回收):

  • 触发条件:老年代空间不足、显式调用System.gc()等。

  • 使用标记 - 清除或标记 - 整理算法,回收整个堆,耗时长

三.JVM 永久代中会发生垃圾回收吗

  • Java8前 永久代

    • 存储位置:JVM堆内存
    • GC 条件:类需满足:所有实例被回收 + 类加载器被回收 + 无反射引用。
    • 问题:容易因类加载过多引发` ‍```rustOutOfMemoryError: PermGen space‍````
  • Java8+ 元空间

    • 存储位置:本地内存,不再占用堆。

    • GC 条件:仅需类加载器被回收,自动释放类元数据。

    • 改进:

      • 内存动态扩展,内存取决于物理内存大小
      • 减少 OOM 风险,GC 效率更高。

四.如何判断对象是否存活?

可达性算法

  • 原理: 从CG Roots出发,遍历所有的引用链,无法到达的对象即为死亡

  • GC Roots 包括:

    • 虚拟机栈中局部变量引用的对象。
    • 方法区中静态变量和常量引用的对象。
    • 本地方法栈中 JNI (Java Native Interface)引用的对象

http://www.mrgr.cn/news/94409.html

相关文章:

  • 网络爬虫相关
  • Docker容器安装软件(完整版)
  • 分布式锁技术全景解析:从传统锁机制到MySQL、Redis/Redisson与ZooKeeper实现
  • 基于Redis实现限流的几种方式
  • 【设计模式】】工厂模式
  • MySQL 企业版 TDE加密后 测试和问题汇总
  • C语言数据结构:数组
  • Ubuntu 24.04 安装与配置 JetBrains Toolbox 指南
  • 算法刷题整理合集(一)
  • C语言【数据结构】:理解什么是数据结构和算法(启航)
  • 【愚公系列】《高效使用DeepSeek》001-什么是DeepSeek
  • 蓝桥杯 之 回溯之充分剪枝
  • Docker基础命令说明
  • 【技术白皮书】内功心法 | 第二部分 | Telnet远程登录的工作原理
  • 芯片研发不需要PPT
  • 计算机视觉|首次写入政府工作报告!这个科技新词“具身智能”到底是什么?
  • 【NLP 33、实践 ⑦ 基于Triple Loss作表示型文本匹配】
  • Linux---VI/VIM编辑器
  • 【算法】数组、链表、栈、队列、树
  • LeetCode 第8题:字符串转换整数 (atoi)