MST
星途 面试题库

面试题:Java内存分配中的对象生命周期与垃圾回收

描述Java对象在内存中的完整生命周期,从创建到被垃圾回收。同时说明垃圾回收机制如何判定对象可以被回收,以及常见的垃圾回收算法有哪些,并简要描述它们的工作原理。
20.3万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

Java对象在内存中的完整生命周期

  1. 创建阶段(Creation)
    • 声明:声明一个对象变量,此时仅仅是在栈中创建了一个引用,例如 Object obj;
    • 实例化:使用 new 关键字在堆内存中分配空间创建对象实例,如 obj = new Object();。在对象创建时,会为对象的成员变量分配内存,并赋予默认值(基本数据类型有默认值,引用类型默认值为 null)。
    • 初始化:执行构造函数,对成员变量进行显式初始化,完成对象的初始化过程。
  2. 使用阶段(Usage): 程序可以通过对象引用访问对象的成员变量和方法,进行各种业务逻辑操作。
  3. 不可达阶段(Inaccessibility): 当对象不再被任何活跃的引用所指向时,它就进入了不可达阶段。例如,将对象的引用赋值为 null,或者引用超出了其作用域,对象就不再能从程序的任何地方访问到。
  4. 垃圾回收阶段(Garbage Collection): 垃圾回收器(GC)检测到对象不可达后,会在合适的时机回收对象所占用的内存空间,将其归还给堆内存,以便后续重新分配。

垃圾回收机制判定对象可被回收的方式

  1. 引用计数法: 为每个对象添加一个引用计数器,每当有一个地方引用该对象时,计数器值加1;当引用失效时,计数器值减1。当计数器值为0时,就认为该对象可以被回收。但这种方法无法解决对象之间循环引用的问题。
  2. 可达性分析算法: 以一系列称为 “GC Roots” 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可达的,即可以被回收。在Java中,可作为GC Roots的对象包括:虚拟机栈(栈帧中的本地变量表)中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中JNI(即一般说的Native方法)引用的对象等。

常见的垃圾回收算法及其工作原理

  1. 标记 - 清除算法(Mark - Sweep)
    • 标记阶段:从 GC Roots 开始遍历,标记所有可达对象。
    • 清除阶段:遍历堆内存,回收所有未被标记的对象(即不可达对象)所占用的空间。这种算法的缺点是会产生大量不连续的内存碎片,导致后续大对象无法分配到足够连续的内存空间。
  2. 复制算法(Copying)
    • 将内存分为大小相等的两块,每次只使用其中一块。当这一块内存用完时,将存活的对象复制到另一块内存上,然后把使用过的内存一次性清理掉。
    • 这种算法的优点是实现简单,运行高效,不会产生内存碎片。缺点是内存利用率低,因为始终只有一半的内存可用。
  3. 标记 - 整理算法(Mark - Compact)
    • 标记阶段:与标记 - 清除算法的标记阶段相同,从 GC Roots 开始标记所有可达对象。
    • 整理阶段:让所有存活的对象向一端移动,然后直接清理掉边界以外的内存,解决了标记 - 清除算法产生内存碎片的问题。
  4. 分代收集算法(Generational Collection)
    • 根据对象存活周期的不同将内存划分为几块,一般分为新生代和老年代。
    • 新生代:对象存活率低,采用复制算法。大部分新创建的对象都在新生代,当新生代空间不足时,触发 Minor GC,将存活对象复制到 Survivor 区或老年代。
    • 老年代:对象存活率高,采用标记 - 整理算法或标记 - 清除算法。当老年代空间不足时,触发 Major GC(也叫 Full GC),对整个堆内存进行垃圾回收。