
垃圾回收(Garbage Collection, GC)是Java虚拟机(JVM)的核心特性之一,它自动管理内存,避免了手动内存管理的复杂性和潜在风险。深入理解GC的核心算法、底层逻辑以及如何根据业务场景进行架构选型,是每一位Java开发者必备的技能。本文将用通俗的语言讲透GC算法的底层原理,结合实战示例帮助你夯实基础并解决实际问题。
GC的核心目标是识别并回收堆内存中不再被引用的对象,释放内存空间。现代GC算法基于两个关键假说:弱分代假说(大多数对象朝生夕死)和强分代假说(熬过多次GC的对象难以死亡)。基于这两个假说,GC算法不断演进,下面我们逐一讲解最核心的三种算法。
标记-清除是最基础的GC算法,分为两个阶段:标记阶段和清除阶段。原理:

优点:实现简单,不需要额外的内存空间。缺点:
复制算法为了解决标记-清除的内存碎片问题而设计。原理:

优点:
标记-整理算法结合了标记-清除和复制算法的优点,适用于老年代。原理:

优点:
基于弱分代假说和强分代假说,现代JVM将堆内存分为年轻代(Young Generation)和老年代(Old Generation),不同代采用不同的GC算法,以提高效率。

年轻代用于存放新创建的对象,大多数对象朝生夕死,因此采用复制算法。年轻代分为Eden区和两个Survivor区(From和To),默认比例为8:1:1。工作流程:
老年代用于存放长期存活的对象,存活对象多,因此采用标记-清除或标记-整理算法。当老年代满时,触发Major GC(老年代GC)或Full GC(整个堆内存GC)。
现代JVM提供了多种垃圾回收器,以满足不同的应用场景。下面我们介绍几种常用的垃圾回收器。
G1是面向服务端的垃圾回收器,适用于大内存(4GB以上)场景,目标是在高吞吐量和低延迟之间取得平衡。特点:
-Xms512m -Xmx512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200
ZGC是JDK 11引入的低延迟垃圾回收器,JDK 15及以后版本正式可用。它的目标是将停顿时间控制在10ms以内,无论堆内存大小(从几百MB到几TB)。特点:
-Xms512m -Xmx512m -XX:+UseZGC
Shenandoah是RedHat开发的低延迟垃圾回收器,与ZGC类似,目标是在大内存场景下实现低延迟。特点:
-Xms512m -Xmx512m -XX:+UseShenandoahGC
选择合适的垃圾回收器需要考虑应用的吞吐量、延迟、内存大小等因素。下面是一些选型建议:
应用场景 | 推荐垃圾回收器 | 关键考虑因素 |
|---|---|---|
后台计算、批处理 | Parallel GC | 高吞吐量,对延迟要求不高 |
Web应用、微服务 | G1 | 平衡吞吐量和延迟,大内存 |
金融交易、实时系统 | ZGC/Shenandoah | 极低延迟,大内存 |
下面我们通过一个Spring Boot应用演示如何配置和观察GC行为。
首先创建一个Maven项目,配置pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.3</version>
<relativePath/>
</parent>
<groupId>com.jam.demo</groupId>
<artifactId>gc-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gc-demo</name>
<description>GC Demo Project</description>
<properties>
<java.version>17</java.version>
<mybatis-plus.version>3.5.5</mybatis-plus.version>
<fastjson2.version>2.0.43</fastjson2.version>
<guava.version>33.0.0-jre</guava.version>
<lombok.version>1.18.30</lombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson2.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
创建主类GcDemoApplication.java:
package com.jam.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GcDemoApplication {
public static void main(String[] args) {
SpringApplication.run(GcDemoApplication.class, args);
}
}
创建Controller类GcDemoController.java:
package com.jam.demo.controller;
import com.jam.demo.service.GcDemoService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/api/gc")
@Tag(name = "GC演示接口", description = "用于演示垃圾回收行为的接口")
publicclass GcDemoController {
@Autowired
private GcDemoService gcDemoService;
@PostMapping("/trigger")
@Operation(summary = "触发GC演示", description = "创建大量对象以触发垃圾回收")
public void triggerGc() {
gcDemoService.create大量Objects();
}
}
创建Service类GcDemoService.java:
package com.jam.demo.service;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
@Slf4j
@Service
publicclass GcDemoService {
privatestaticfinalint OBJECT_COUNT = 100000;
publicvoid create大量Objects() {
List<byte[]> tempList = Lists.newArrayListWithCapacity(OBJECT_COUNT);
for (int i = 0; i < OBJECT_COUNT; i++) {
tempList.add(newbyte[1024]);
}
log.info("已创建{}个1KB的byte数组", OBJECT_COUNT);
tempList.clear();
System.gc();
log.info("已触发System.gc()");
}
}
创建配置文件application.yml:
server:
port:8080
spring:
application:
name:gc-demo
springdoc:
api-docs:
path:/v3/api-docs
swagger-ui:
path:/swagger-ui.html
使用以下JVM参数运行应用:
-Xms512m -Xmx512m -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log
启动应用后,访问Swagger UI(http://localhost:8080/swagger-ui.html),调用/api/gc/trigger接口,观察控制台日志和gc.log文件。可以使用GCEasy(https://gceasy.io/)工具分析gc.log文件,查看GC的次数、停顿时间、内存使用情况等。
本文深入讲解了垃圾回收的核心算法(标记-清除、复制、标记-整理)、分代收集理论、现代垃圾回收器(G1、ZGC、Shenandoah)以及架构选型建议,并通过实战示例演示了如何配置和观察GC行为。理解GC的底层逻辑,根据业务场景选择合适的垃圾回收器,对于优化应用性能、提高稳定性至关重要。希望本文能帮助你夯实GC基础,解决实际问题。