Java的内存管理是其强大功能之一,而垃圾回收(Garbage Collection, GC)是确保内存不会被无用对象占用的关键机制。本文将详细介绍三种常见的垃圾回收算法:标记-清除、复制、标记-整理,并通过代码示例展示这些算法的具体应用。
1. 标记-清除算法(Mark-Sweep)
标记-清除算法是最基础的垃圾回收算法之一。它分为两个阶段:
- 标记阶段:遍历所有的对象,从根对象开始标记所有可达的对象。
- 清除阶段:遍历内存中的所有对象,清除那些没有被标记的对象。
特点:
- 优点:实现简单,不需要额外的空间分配。
- 缺点:标记和清除阶段都会遍历所有对象,效率较低。另外,清除阶段会产生内存碎片,导致后续的内存分配速度变慢。
代码示例:
import java.util.HashSet;
import java.util.Set;
class ObjectReference {
ObjectReference next;
boolean isMarked = false;
}
class Heap {
private Set<ObjectReference> objects = new HashSet<>();
public void addObject(ObjectReference obj) {
objects.add(obj);
}
public void markAndSweep() {
// 标记阶段
for (ObjectReference obj : objects) {
mark(obj);
}
// 清除阶段
objects.removeIf(obj -> !obj.isMarked);
}
private void mark(ObjectReference obj) {
if (obj != null && !obj.isMarked) {
obj.isMarked = true;
mark(obj.next);
}
}
public void resetMarks() {
for (ObjectReference obj : objects) {
obj.isMarked = false;
}
}
}
public class MarkSweepDemo {
public static void main(String[] args) {
Heap heap = new Heap();
ObjectReference obj1 = new ObjectReference();
ObjectReference obj2 = new ObjectReference();
ObjectReference obj3 = new ObjectReference();
obj1.next = obj2;
obj2.next = obj3;
heap.addObject(obj1);
heap.addObject(obj2);
heap.addObject(obj3);
heap.markAndSweep();
// 清除完成后的堆状态
}
}
2. 复制算法(Copying)
复制算法通过将存活对象从一个区域复制到另一个区域来实现内存回收。它将内存分为两块:From
和 To
。垃圾回收时,将存活对象从 From
复制到 To
,然后交换两块区域的角色。
特点:
- 优点:没有内存碎片,内存分配简单高效。
- 缺点:需要双倍的内存空间来存储对象,复制过程时间复杂度较高。
代码示例:
import java.util.ArrayList;
import java.util.List;
class CopyingGC {
private List<ObjectReference> fromSpace = new ArrayList<>();
private List<ObjectReference> toSpace = new ArrayList<>();
public void addObject(ObjectReference obj) {
fromSpace.add(obj);
}
public void copy() {
for (ObjectReference obj : fromSpace) {
if (isReachable(obj)) {
toSpace.add(obj);
}
}
fromSpace.clear();
List<ObjectReference> temp = fromSpace;
fromSpace = toSpace;
toSpace = temp;
}
private boolean isReachable(ObjectReference obj) {
// 模拟是否可达
// 在实际应用中,这里需要遍历GC根节点来判断
return obj != null;
}
}
public class CopyingDemo {
public static void main(String[] args) {
CopyingGC gc = new CopyingGC();
ObjectReference obj1 = new ObjectReference();
ObjectReference obj2 = new ObjectReference();
ObjectReference obj3 = new ObjectReference();
obj1.next = obj2;
obj2.next = obj3;
gc.addObject(obj1);
gc.addObject(obj2);
gc.addObject(obj3);
gc.copy();
// 复制完成后的堆状态
}
}
3. 标记-整理算法(Mark-Compact)
标记-整理算法结合了标记-清除和复制算法的优点。它通过标记阶段标记存活对象,然后将所有存活对象压缩到内存的一端,清除无用对象。
特点:
- 优点:无内存碎片,内存占用更高效。
- 缺点:需要额外的步骤来整理内存,复杂度较高。
代码示例:
import java.util.ArrayList;
import java.util.List;
class MarkCompactGC {
private List<ObjectReference> heap = new ArrayList<>();
public void addObject(ObjectReference obj) {
heap.add(obj);
}
public void markCompact() {
// 标记阶段
for (ObjectReference obj : heap) {
mark(obj);
}
// 整理阶段
int freeIndex = 0;
for (int i = 0; i < heap.size(); i++) {
if (heap.get(i).isMarked) {
heap.set(freeIndex, heap.get(i));
freeIndex++;
}
}
// 移动未标记的部分
for (int i = freeIndex; i < heap.size(); i++) {
heap.set(i, null);
}
// 重置标记
for (ObjectReference obj : heap) {
if (obj != null) {
obj.isMarked = false;
}
}
}
private void mark(ObjectReference obj) {
if (obj != null && !obj.isMarked) {
obj.isMarked = true;
mark(obj.next);
}
}
}
public class MarkCompactDemo {
public static void main(String[] args) {
MarkCompactGC gc = new MarkCompactGC();
ObjectReference obj1 = new ObjectReference();
ObjectReference obj2 = new ObjectReference();
ObjectReference obj3 = new ObjectReference();
obj1.next = obj2;
obj2.next = obj3;
gc.addObject(obj1);
gc.addObject(obj2);
gc.addObject(obj3);
gc.markCompact();
// 整理完成后的堆状态
}
}
不同垃圾回收算法的对比
算法 | 优点 | 缺点 |
---|---|---|
标记-清除 | 实现简单,不需要额外空间 | 产生内存碎片,效率低 |
复制算法 | 没有内存碎片,分配效率高 | 需要双倍内存空间,复制时间复杂度高 |
标记-整理 | 没有内存碎片,内存占用高效 | 需要额外步骤整理内存,复杂度高 |
结论
不同的垃圾回收算法在不同的场景下有不同的适用性。标记-清除算法实现简单,但容易产生内存碎片;复制算法避免了内存碎片,但需要额外的内存空间;标记-整理算法综合了两者的优点,但实现复杂度较高。选择合适的垃圾回收算法可以提高Java应用的性能和稳定性。了解这些算法的工作原理和优缺点,将有助于更好地进行Java应用的性能调优。