垃圾回收
垃圾回收(Garbage Collection, GC)是JVM自动管理内存的机制,负责回收不再使用的对象占用的内存空间。Java开发者不需要手动释放内存,这大大减少了内存泄漏和指针错误的风险。
为什么需要垃圾回收
- 防止内存泄漏
- 避免手动内存管理的复杂性
- 提高开发效率
- 保证程序稳定性
垃圾回收的基本原理
对象存活判断
引用计数法(Java未采用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Object { int refCount = 0;
void addReference() { refCount++; }
void removeReference() { refCount--; if (refCount == 0) { } } }
|
可达性分析算法(Java采用)
从GC Roots对象开始,向下搜索引用链,不在引用链上的对象被视为可回收。
GC Roots包括:
- 虚拟机栈中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
引用类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import java.lang.ref.*; public class ReferenceTypes { public static void main(String[] args) { Object strongRef = new Object();
SoftReference<Object> softRef = new SoftReference<>(new Object());
WeakReference<Object> weakRef = new WeakReference<>(new Object());
ReferenceQueue<Object> queue = new ReferenceQueue<>(); PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);
System.out.println("Strong: " + strongRef); System.out.println("Soft: " + softRef.get()); System.out.println("Weak: " + weakRef.get()); System.out.println("Phantom: " + phantomRef.get());
System.gc(); System.out.println("After GC:"); System.out.println("Strong: " + strongRef); System.out.println("Soft: " + softRef.get()); System.out.println("Weak: " + weakRef.get()); System.out.println("Phantom: " + phantomRef.get()); } }
|
垃圾回收算法
标记-清除算法
缺点:产生内存碎片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void markAndSweep() { for (Object obj : heap) { if (isReachable(obj)) { obj.marked = true; } }
for (Object obj : heap) { if (!obj.marked) { free(obj); } } }
|
复制算法
将内存分为两块,只使用其中一块。GC时将存活对象复制到另一块,然后清除当前块。
优点:无内存碎片
缺点:内存利用率低
1 2 3 4 5 6 7 8 9
| void copy() { for (Object obj : fromSpace) { if (isReachable(obj)) { copyTo(obj, toSpace); } } swap(fromSpace, toSpace); clear(toSpace); }
|
标记-整理算法
优点:无内存碎片,内存利用率高
缺点:移动对象成本高
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| void markAndCompact() { for (Object obj : heap) { if (isReachable(obj)) { obj.marked = true; } }
int newAddr = 0; for (Object obj : heap) { if (obj.marked) { move(obj, newAddr); newAddr += obj.size; } }
freeMemoryFrom(newAddr); }
|
JVM内存分代与GC类型
堆内存结构
1 2 3 4 5 6
| Young Generation (1/3 heap) ├─ Eden (80%) ├─ Survivor 0 (10%) └─ Survivor 1 (10%) Old Generation (2/3 heap) PermGen/Metaspace (非堆)
|
GC类型
- Minor GC/Young GC:只收集年轻代
- Major GC/Old GC:只收集老年代
- Full GC:收集整个堆(年轻代+老年代+方法区)
对象分配与晋升流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class ObjectLifecycle { public static void main(String[] args) { Object obj1 = new Object();
for (int i = 0; i < 100000; i++) { new Object(); }
Object longLivedObj = new Object(); for (int i = 0; i < 15; i++) { System.gc(); } } }
|
垃圾收集器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 串行收集器 -XX:+UseSerialGC
并行收集器(吞吐量优先) -XX:+UseParallelGC -XX:+UseParallelOldGC
CMS收集器(低延迟) -XX:+UseConcMarkSweepGC
G1收集器(平衡型) -XX:+UseG1GC
ZGC和Shenandoah(超低延迟) -XX:+UseZGC (Java 11+) -XX:+UseShenandoahGC
|
GC调优示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class GCParameters { public static void main(String[] args) { System.out.println("GC Demo");
for (int i = 0; i < 100000; i++) { new Object(); }
System.gc(); } }
|
设置GC参数示例
1 2 3 4
| java -XX:+UseG1GC -Xms512m -Xmx512m -XX:+PrintGCDetails GCParameters
java -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:GCTimeRatio=99 -XX:+PrintGCDetails GCParameters
|
常见GC问题与解决方案
内存泄漏
解决方案:及时清除不再需要的引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import java.util.ArrayList; import java.util.List; public class MemoryLeak { static List<Object> list = new ArrayList<>();
public static void main(String[] args) { while (true) { list.add(new byte[1024 * 1024]); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
|
频繁Full GC问题
可能原因:
- 老年代空间不足
- System.gc()调用
- 大对象直接进入老年代
解决方案:
- 增加堆大小
- 调整新生代与老年代比例
- 避免手动调用System.gc()
现代GC发展趋势
- 低延迟GC:ZGC和Shenandoah的目标是控制在10ms以内的停顿时间
- 云原生GC:适应容器环境的内存弹性
- AI驱动的GC:根据应用特点自动优化GC参数
Java的垃圾回收机制是一个复杂但设计精妙的系统,理解其工作原理对于编写高性能Java应用程序至关重要。不同的应用场景可能需要不同的GC策略和调优方法。建议在实际开发中结合监控工具(如VisualVM、JConsole、GC日志等)来分析应用的内存使用情况,并据此进行调优。