面试题答案
一键面试Java对象在内存中的完整生命周期
- 创建阶段(Creation):
- 声明:声明一个对象变量,此时仅仅是在栈中创建了一个引用,例如
Object obj;
。 - 实例化:使用
new
关键字在堆内存中分配空间创建对象实例,如obj = new Object();
。在对象创建时,会为对象的成员变量分配内存,并赋予默认值(基本数据类型有默认值,引用类型默认值为null
)。 - 初始化:执行构造函数,对成员变量进行显式初始化,完成对象的初始化过程。
- 声明:声明一个对象变量,此时仅仅是在栈中创建了一个引用,例如
- 使用阶段(Usage): 程序可以通过对象引用访问对象的成员变量和方法,进行各种业务逻辑操作。
- 不可达阶段(Inaccessibility):
当对象不再被任何活跃的引用所指向时,它就进入了不可达阶段。例如,将对象的引用赋值为
null
,或者引用超出了其作用域,对象就不再能从程序的任何地方访问到。 - 垃圾回收阶段(Garbage Collection): 垃圾回收器(GC)检测到对象不可达后,会在合适的时机回收对象所占用的内存空间,将其归还给堆内存,以便后续重新分配。
垃圾回收机制判定对象可被回收的方式
- 引用计数法: 为每个对象添加一个引用计数器,每当有一个地方引用该对象时,计数器值加1;当引用失效时,计数器值减1。当计数器值为0时,就认为该对象可以被回收。但这种方法无法解决对象之间循环引用的问题。
- 可达性分析算法: 以一系列称为 “GC Roots” 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可达的,即可以被回收。在Java中,可作为GC Roots的对象包括:虚拟机栈(栈帧中的本地变量表)中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中JNI(即一般说的Native方法)引用的对象等。
常见的垃圾回收算法及其工作原理
- 标记 - 清除算法(Mark - Sweep):
- 标记阶段:从 GC Roots 开始遍历,标记所有可达对象。
- 清除阶段:遍历堆内存,回收所有未被标记的对象(即不可达对象)所占用的空间。这种算法的缺点是会产生大量不连续的内存碎片,导致后续大对象无法分配到足够连续的内存空间。
- 复制算法(Copying):
- 将内存分为大小相等的两块,每次只使用其中一块。当这一块内存用完时,将存活的对象复制到另一块内存上,然后把使用过的内存一次性清理掉。
- 这种算法的优点是实现简单,运行高效,不会产生内存碎片。缺点是内存利用率低,因为始终只有一半的内存可用。
- 标记 - 整理算法(Mark - Compact):
- 标记阶段:与标记 - 清除算法的标记阶段相同,从 GC Roots 开始标记所有可达对象。
- 整理阶段:让所有存活的对象向一端移动,然后直接清理掉边界以外的内存,解决了标记 - 清除算法产生内存碎片的问题。
- 分代收集算法(Generational Collection):
- 根据对象存活周期的不同将内存划分为几块,一般分为新生代和老年代。
- 新生代:对象存活率低,采用复制算法。大部分新创建的对象都在新生代,当新生代空间不足时,触发 Minor GC,将存活对象复制到 Survivor 区或老年代。
- 老年代:对象存活率高,采用标记 - 整理算法或标记 - 清除算法。当老年代空间不足时,触发 Major GC(也叫 Full GC),对整个堆内存进行垃圾回收。