可达性分析是一种用于判定对象是否可以被垃圾回收的算法。基本思想是通过一个称为“根集合”(GC Root)的起点集合,来追踪对象引用。如果一个对象从根集合开始可以被追踪到,那么它就是“可达”的,否则就是“不可达”的,可以被认为是垃圾,因而可以被回收。
GC Root是可达性分析的起点。在Java中,以下几种类型的对象可以作为GC Root:
- Java栈中的引用对象:即当前正在被调用的方法中的局部变量和输入参数。
- 静态属性引用的对象:即所有类的静态属性(静态变量)。
- 常量引用的对象:即常量池中的引用(如字符串常量)。
- 本地方法栈中的引用对象:即本地方法(Native方法)中引用的对象。
- 活动线程:所有正在运行的线程。
纯粹的Spring Boot
在Spring Boot
构建的软件项目中,GC Root的概念与在任何其他Java应用程序中相同,但由于Spring Boot的结构和特性,有一些特定的地方可能成为GC Root。以下是Spring Boot
项目中特别需要注意的几个GC Root来源:
-
应用程序上下文 (Application Context)
Spring Boot应用程序上下文持有许多Bean的引用。这些Bean通常包括服务、控制器、配置等。由于应用程序上下文是通过静态方法访问的,它们往往会作为GC Root。 -
静态变量
任何静态变量都可能作为GC Root。例如,在Spring Bean中声明的静态字段或通过静态方法访问的资源。 -
线程池
Spring Boot应用程序通常使用线程池来处理并发请求。线程池中的线程会引用许多对象,这些线程会作为GC Root,防止这些对象被回收。 -
Servlet容器中的对象
Spring Boot通常嵌入一个Servlet容器(如Tomcat、Jetty)。这些容器中的对象(如HttpSession、ServletContext等)也可能成为GC Root。 -
JVM本地方法栈
任何在Spring Boot应用程序中使用的JNI(Java Native Interface)调用所引用的对象,也会作为GC Root。
@SpringBootApplication
public class SpringBootGCRootDemoApplication {
// 静态变量,GC Root
private static MyService myStaticService;
@Autowired
private MyService myService;
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(SpringBootGCRootDemoApplication.class, args);
// 应用程序上下文中的Bean,GC Root
myStaticService = context.getBean(MyService.class);
}
@RestController
@RequestMapping("/api")
public static class MyController {
// 控制器中的依赖,GC Root
@Autowired
private MyService myService;
@GetMapping("/hello")
public String sayHello() {
return myService.getMessage();
}
}
@Service
public static class MyService {
public String getMessage() {
return "Hello, World!";
}
}
}
在Spring Boot构建的软件项目中,结合Spring、Spring MVC、MyBatis等框架来实现三层模型(MVC模式),以下是这些框架中的一些关键组件以及它们如何成为GC Root的解释:
三层模型(MVC模式)
通常的三层架构分为:
- 表示层(Presentation Layer):处理用户的请求和响应。
- 业务逻辑层(Business Logic Layer):处理具体的业务逻辑。
- 数据访问层(Data Access Layer):处理数据库访问。
以下是Spring Boot项目中使用Spring、Spring MVC、MyBatis等框架时,哪些组件会成为GC Root:
表示层(Presentation Layer)
-
Spring MVC 控制器 (Controllers)
- 控制器类和它们的实例通常是单例(Singleton),并且由Spring容器管理。
- 这些实例通过Spring上下文持有,会成为GC Root。
@RestController @RequestMapping("/api") public class MyController { @Autowired private MyService myService; @GetMapping("/hello") public String sayHello() { return myService.getMessage(); } }
-
Servlet 容器中的对象
- Spring Boot内嵌的Servlet容器(如Tomcat、Jetty)会维护一些长生命周期的对象,如
HttpSession
、ServletContext
,这些也会成为GC Root。
- Spring Boot内嵌的Servlet容器(如Tomcat、Jetty)会维护一些长生命周期的对象,如
业务逻辑层(Business Logic Layer)
-
Spring Service 层的Bean
- Service类通常也是单例,并且由Spring容器管理。
- 这些Bean通过Spring上下文持有,会成为GC Root。
@Service public class MyService { public String getMessage() { return "Hello, World!"; } }
数据访问层(Data Access Layer)
-
MyBatis Mapper
- MyBatis Mapper接口的实现由MyBatis框架生成,并通过Spring容器管理。
- 这些Mapper对象被Service层引用,而Service层又被控制器引用,间接地,这些Mapper对象会成为GC Root的一部分。
@Mapper public interface MyMapper { @Select("SELECT * FROM my_table WHERE id = #{id}") MyEntity getById(int id); }
-
数据库连接和会话
- 数据库连接池中的连接对象(如HikariCP、C3P0)和MyBatis的SqlSession对象,由于其生命周期管理,这些对象也会成为GC Root。
示例代码总结
以下是一个综合示例,展示Spring Boot项目中如何使用这些组件:
@SpringBootApplication
public class SpringBootGCRootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootGCRootDemoApplication.class, args);
}
@RestController
@RequestMapping("/api")
public static class MyController {
@Autowired
private MyService myService;
@GetMapping("/hello")
public String sayHello() {
return myService.getMessage();
}
}
@Service
public static class MyService {
@Autowired
private MyMapper myMapper;
public String getMessage() {
MyEntity entity = myMapper.getById(1);
return "Hello, " + entity.getName();
}
}
@Mapper
public interface MyMapper {
@Select("SELECT * FROM my_table WHERE id = #{id}")
MyEntity getById(int id);
}
public static class MyEntity {
private int id;
private String name;
// getters and setters
}
}
总结
在Spring Boot应用中,以下组件通常会作为GC Root:
- Spring上下文管理的控制器、服务和Mapper
- Servlet容器中的长生命周期对象
- 数据库连接池中的连接对象
- 活动线程
这些组件和对象由于它们在应用程序中的关键角色,会被GC Root引用,从而不会被垃圾回收。理解这些GC Root的来源,对于调试和优化Spring Boot应用程序的内存使用非常重要。