文章目录
判断对象存活的算法
引用计数(Reference Counting):
- 算法为每个对象维护一个引用计数器,每当有一个新的引用指向该对象时,计数器加1;当引用失效时,计数器减1。
- 优点:实现简单,实时性较好。
- 缺点:不能处理循环引用的情况,且计数操作有一定的开销。
在实际项目中实现引用计数
以下是一个简单的例子,展示了如何在Java中实现一个基本的引用计数对象:
public class RefCounted {
private int referenceCount = 0;
private Object data;
public RefCounted(Object data) {
this.data = data;
referenceCount = 1; // 初始引用计数为1
}
public void addReference() {
referenceCount++; // 增加引用计数
}
public void removeReference() {
referenceCount--; // 减少引用计数
if (referenceCount == 0) {
// 如果引用计数为0,则回收资源
data = null;
System.out.println("Object has been garbage collected.");
}
}
public Object getData() {
return data;
}
}
RefCounted 类包含了一个引用计数器和一个数据对象。每次创建一个新的引用时,都应该调用 addReference() 方法,每次引用失效时,都应该调用 removeReference() 方法。当引用计数降为0时,表示没有活动的引用指向该对象,可以执行相应的清理操作。
使用这个类的示例:
public class ReferenceCountingExample {
public static void main(String[] args) {
RefCounted refCounted = new RefCounted("Important data");
// 增加引用
refCounted.addReference();
// 使用数据...
System.out.println(refCounted.getData());
// 减少引用
refCounted.removeReference();
// 减少引用
refCounted.removeReference();
// 此时,引用计数为0,对象应该已经被清理
}
}
标记-清除(Mark-Sweep):
- 算法分为“标记”和“清除”两个阶段。标记阶段遍历所有可达对象,并做上标记;清除阶段遍历堆,回收未被标记的对象所占用的空间。
- 优点:解决了引用计数法的循环引用问题。
- 缺点:回收过程中,用户线程需要暂停(Stop-The-World),可能会产生内存碎片。
在实际项目中实现标记-清除
以下是一个简化的例子,展示了标记-清除算法的基本步骤:
- 标记(Mark):遍历所有可达对象,并标记它们为活动的。
- 清除(Sweep):遍历堆,回收未被标记的对象所占用的空间。
import java.util.ArrayList;
import java.util.List;
class GCObject {
public List<GCObject> references;
public boolean marked;
public GCObject() {
references = new ArrayList<>();
marked = false;
}
}
public class MarkSweepGC {
private static List<GCObject> heap = new ArrayList<>();
public static void main(String[] args) {
// 创建一些对象并建立引用关系
GCObject a = new GCObject();
GCObject b = new GCObject();
GCObject c = new GCObject();
a.references.add(b);
b.references.add(c);
c.references.add(a); // 创建一个循环引用
// 将对象添加到堆中
heap.add(a);
heap.add(b);
heap.add(c);
// 执行标记-清除垃圾回收
markSweep();
// 检查堆中的对象
for (GCObject obj : heap) {
if (obj.marked) {
System.out.println("Object is still alive.");
} else {
System.out.println("Object has been collected.");
}
}
}
public static void markSweep() {
// 标记阶段
mark(heap.get(0));
// 清除阶段
for (int i = heap.size() - 1; i >= 0; i--) {
GCObject obj = heap.get(i);
if (!obj.marked) {
heap.remove(i);
System.out.println("Collected object " + obj);
} else {
obj.marked = false; // 重置标记,为下一次垃圾回收做准备
}
}
}
public static void mark(GCObject obj) {
if (obj.marked) {
return;
}
obj.marked = true;
for (GCObject reference : obj.references) {
mark(reference);
}
}
}
标记-整理(Mark-Compact):
- 是标记-清除算法的改进,标记阶段相同,清除阶段在回收前会先执行整理操作,将所有存活的对象移动到内存的一端,然后清理掉边界以外的内存。
- 优点:避免了内存碎片。
- 缺点:整理阶段的开销较大。
在实际项目中实现标记-整理
以下是一个简化的例子,展示了标记-整理算法的基本步骤:
- 标记(Mark):遍历所有可达对象,并标记它们为活动的。
- 整理(Compact):将所有活动的对象移动到内存的一端,更新它们的引用,然后清理掉边界以外的内存。
import java.util.ArrayList;
import java.util.List;
class GCObject {
public List<GCObject> references;
public boolean marked;
public int index;
public GCObject() {
references = new ArrayList<>();
marked = false;
index = -1;
}
}
public class MarkCompactGC {
private static List<GCObject> heap = new ArrayList<>();
private static int nextIndex = 0;
public static void main(String[] args) {
// 创建一些对象并建立引用关系
GCObject a = new GCObject();
GCObject b = new GCObject();
GCObject c = new GCObject();
a.references.add(b);
b.references.add(c);
c.references.add(a); // 创建一个循环引用
// 将对象添加到堆中
addObjectToHeap(a);
addObjectToHeap(b);
addObjectToHeap(c);
// 执行标记-整理垃圾回收
markCompact();
// 检查堆中的对象
for (GCObject obj : heap) {
if (obj.index != -1) {
System.out.println("Object at index " + obj.index + " is still alive.");
} else {
System.out.println("Object has been collected.");
}
}
}
public static void addObjectToHeap(GCObject obj) {
obj.index = nextIndex++;
heap.add(obj);
}
public static void markCompact() {
// 标记阶段
mark(heap.get(0));
// 整理阶段
int aliveCount = 0;
for (GCObject obj : heap) {
if (obj.marked) {
obj.index = aliveCount++;
}
}
// 清理阶段
for (int i = heap.size() - 1; i >= 0; i--) {
GCObject obj = heap.get(i);
if (obj.index == -1) {
heap.remove(i);
System.out.println("Collected object " + obj);
} else {
// 更新引用
int newIndex = obj.index;
for (GCObject reference : obj.references) {
reference.references.set(reference.references.indexOf(obj), heap.get(newIndex));
}
obj.marked = false; // 重置标记,为下一次垃圾回收做准备
}
}
}
public static void mark(GCObject obj) {
if (obj.marked) {
return;
}
obj.marked = true;
for (GCObject reference : obj.references) {
mark(reference);
}
}
}
分代收集(Generational Collection):
- 算法基于对象存活周期的不同将内存划分为几代,对年轻代和老年代采用不同的垃圾回收策略。
- 通常年轻代使用标记-清除或复制算法,因为年轻代对象死亡率高;老年代使用标记-清除或标记-整理算法。
- 优点:提高了垃圾回收的效率。
在实际项目中实现分代收集
以下是一个简化的例子,展示了分代收集的基本步骤:
- 新生代(Minor GC):使用复制算法对新生代进行垃圾回收。
- 老年代(Major GC/Old GC):使用标记-清除或标记-整理算法对老年代进行垃圾回收。
- 全堆(Full GC):对整个堆进行垃圾回收,包括新生代和老年代。
import java.util.ArrayList;
import java.util.List;
class GCObject {
public List<GCObject> references;
public boolean marked;
public boolean inYoungGeneration;
public GCObject() {
references = new ArrayList<>();
marked = false;
inYoungGeneration = true;
}
}
public class GenerationalGC {
private static List<GCObject> youngGen = new ArrayList<>();
private static List<GCObject> oldGen = new ArrayList<>();
public static void main(String[] args) {
// 创建一些对象并建立引用关系
GCObject a = new GCObject();
GCObject b = new GCObject();
GCObject c = new GCObject();
a.references.add(b);
b.references.add(c);
c.references.add(a); // 创建一个循环引用
// 将对象添加到新生代
youngGen.add(a);
youngGen.add(b);
youngGen.add(c);
// 模拟对象年龄增长
for (int i = 0; i < 10; i++) {
minorGC();
promoteToOldGen();
}
// 执行老年代垃圾回收
majorGC();
// 检查老年代中的对象
for (GCObject obj : oldGen) {
if (obj.marked) {
System.out.println("Object in old generation is still alive.");
} else {
System.out.println("Object in old generation has been collected.");
}
}
}
public static void minorGC() {
// 在新生代中使用复制算法进行垃圾回收
List<GCObject> survivors = new ArrayList<>();
for (GCObject obj : youngGen) {
if (obj.marked) {
survivors.add(obj);
obj.marked = false; // 重置标记
}
}
youngGen = survivors;
}
public static void promoteToOldGen() {
// 将新生代中的对象晋升到老年代
for (GCObject obj : youngGen) {
obj.inYoungGeneration = false;
oldGen.add(obj);
}
youngGen.clear();
}
public static void majorGC() {
// 在老年代中使用标记-清除算法进行垃圾回收
mark(oldGen.get(0));
for (int i = oldGen.size() - 1; i >= 0; i--) {
GCObject obj = oldGen.get(i);
if (!obj.marked) {
oldGen.remove(i);
System.out.println("Collected object from old generation " + obj);
} else {
obj.marked = false; // 重置标记
}
}
}
public static void mark(GCObject obj) {
if (obj.marked) {
return;
}
obj.marked = true;
for (GCObject reference : obj.references) {
mark(reference);
}
}
}
增量收集(Incremental Collection):
- 将垃圾回收分成多个小步骤,交替执行与用户程序的运行,每次只做一小部分垃圾回收工作,减少单次垃圾回收时的停顿时间。
- 优点:减少了应用程序的停顿时间。
- 缺点:总体上可能会增加垃圾回收的开销,因为需要更多的总时间和上下文切换。
在实际项目中实现增量收集
以下是一个简化的例子,展示了如何在Java中模拟增量收集的基本概念。请注意,这个例子并不是一个完整的增量收集实现,而是为了说明增量收集的基本原理。
import java.util.ArrayList;
import java.util.List;
class GCObject {
public List<GCObject> references;
public boolean marked;
public GCObject() {
references = new ArrayList<>();
marked = false;
}
}
public class IncrementalGC {
private static List<GCObject> heap = new ArrayList<>();
private static int threshold; // 设定每次增量收集处理的对象数量上限
public static void main(String[] args) {
// 创建一些对象并建立引用关系
GCObject a = new GCObject();
GCObject b = new GCObject();
GCObject c = new GCObject();
a.references.add(b);
b.references.add(c);
c.references.add(a); // 创建一个循环引用
// 将对象添加到堆中
heap.add(a);
heap.add(b);
heap.add(c);
// 设置阈值
threshold = 1; // 每次只处理一个对象的引用
// 执行增量垃圾回收
incrementalGC();
// 检查堆中的对象
for (GCObject obj : heap) {
if (obj.marked) {
System.out.println("Object is still alive.");
} else {
System.out.println("Object has been collected.");
}
}
}
public static void incrementalGC() {
// 标记阶段
for (GCObject obj : heap) {
if (!obj.marked) {
mark(obj);
if (--threshold <= 0) {
break; // 达到本次处理的阈值上限
}
}
}
// 清除阶段
for (int i = heap.size() - 1; i >= 0; i--) {
GCObject obj = heap.get(i);
if (!obj.marked) {
heap.remove(i);
System.out.println("Collected object " + obj);
} else {
obj.marked = false; // 重置标记
}
}
}
public static void mark(GCObject obj) {
if (obj.marked) {
return;
}
obj.marked = true;
for (GCObject reference : obj.references) {
mark(reference);
}
}
}
并发收集(Concurrent Collection):
- 算法允许垃圾回收线程与用户线程同时运行。
- 优点:减少了应用程序的停顿时间。
- 缺点:需要更复杂的算法来保证线程安全,且可能会影响系统的吞吐量。
在实际项目中实现并发收集
以下是一个简化的例子,展示了如何在Java中模拟并发收集的基本概念。请注意,这个例子并不是一个完整的并发收集实现,而是为了说明并发收集的基本原理。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
class GCObject {
public List<GCObject> references;
public AtomicBoolean marked;
public GCObject() {
references = new ArrayList<>();
marked = new AtomicBoolean(false);
}
}
public class ConcurrentGC {
private static List<GCObject> heap = new ArrayList<>();
public static void main(String[] args) {
// 创建一些对象并建立引用关系
GCObject a = new GCObject();
GCObject b = new GCObject();
GCObject c = new GCObject();
a.references.add(b);
b.references.add(c);
c.references.add(a); // 创建一个循环引用
// 将对象添加到堆中
heap.add(a);
heap.add(b);
heap.add(c);
// 并发标记阶段
markConcurrently();
// 并发清除阶段
collectConcurrently();
// 检查堆中的对象
for (GCObject obj : heap) {
if (obj.marked.get()) {
System.out.println("Object is still alive.");
} else {
System.out.println("Object has been collected.");
}
}
}
public static void markConcurrently() {
for (GCObject obj : heap) {
if (obj.marked.compareAndSet(false, true)) {
for (GCObject reference : obj.references) {
reference.marked.set(true);
}
}
}
}
public static void collectConcurrently() {
for (int i = heap.size() - 1; i >= 0; i--) {
GCObject obj = heap.get(i);
if (!obj.marked.get()) {
heap.remove(i);
System.out.println("Collected object " + obj);
} else {
obj.marked.set(false); // 重置标记
}
}
}
}
本文含有隐藏内容,请 开通VIP 后查看