HotSpot 虚拟机垃圾收集调优总结

工作中我们可能需要对 HotSpot 虚拟机垃圾收集调优,那么应该怎么做呢?

Oracle 在 HotSpot Virtual Machine Garbage Collection Tuning Guide 提到过一个调优策略:

Tuning Strategy

Do not choose a maximum value for the heap unless you know that you need a heap greater than the default maximum heap size. Choose a throughput goal that is sufficient for your application.

The heap will grow or shrink to a size that will support the chosen throughput goal. A change in the application’s behavior can cause the heap to grow or shrink. For example, if the application starts allocating at a higher rate, the heap will grow to maintain the same throughput.

If the heap grows to its maximum size and the throughput goal is not being met, the maximum heap size is too small for the throughput goal. Set the maximum heap size to a value that is close to the total physical memory on the platform but which does not cause swapping of the application. Execute the application again. If the throughput goal is still not met, then the goal for the application time is too high for the available memory on the platform.

If the throughput goal can be met, but there are pauses that are too long, then select a maximum pause time goal. Choosing a maximum pause time goal may mean that your throughput goal will not be met, so choose values that are an acceptable compromise for the application.

It is typical that the size of the heap will oscillate as the garbage collector tries to satisfy competing goals. This is true even if the application has reached a steady state. The pressure to achieve a throughput goal (which may require a larger heap) competes with the goals for a maximum pause time and a minimum footprint (which both may require a small heap).



MyBatis 的工作流程(一)

MyBatis 的工作流程主要是以下几步:

  • 加载配置并初始化
  • 接收调用请求
  • 处理操作请求
  • 返回处理结果

本文我们先具体来看看它与 Spring Boot 集成时的初始化。

MyBatis 官方团队有开发一个 Spring Boot Starter,我们通过它的代码来看下配置加载和初始化。配置的入口是 MybatisAutoConfiguration,它会按需创建 sqlSessionFactory 和 sqlSessionTemplate 这两个 bean 对象,同时它还包含一个内部配置类 MapperScannerRegistrarNotFoundConfiguration:

@ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {

  public void afterPropertiesSet() {
        "Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");


MapperScannerRegistrarNotFoundConfiguration 导入了 AutoConfiguredMapperScannerRegistrar,它会向 IoC 容器注册 MapperScannerConfigurer。


Spring MVC 中的异常处理

使用 Spring 来开发 web 应用时很有必要建立一个统一的异常处理体系。想要建立这个体系,我们先要搞清楚 Spring MVC 中的异常处理机制。 Spring MVC 是基于 Servlet,所以它遵循 Servlet 规范。

Servlet 规范中有详细的错误处理说明,简单来说就是 Servlet 在处理请求时可能会抛出异常或者调用 sendError ,这时 Servlet-Container 就要产生相应的错误界面,错误界面是允许自定义的。Spring MVC 的核心之一是 DispatchServlet,它是一个前端控制器,所有的请求处理都由它来驱动,从名字可以看出,它也是一个 Servlet,所以它的错误处理机制自然要遵循 Servlet 规范。从完整性的角度来看,还一种错误处理方法,Servlet 可以自己设置 HTTP 的 status code 和 body,也就是不和 Servlet-Container 联动来处理错误,而是完全自主地处理。

我们先来看 DispatcherServlet 的异常处理机制,Spring 团队将异常处理功能集中到 HandlerExceptionResolver 接口的实现类中,DispatcherServlet 在初始化过程会把 IoC 容器中所有的 HandlerExceptionResolver 的实现类排好序后组装起来用于异常处理。

现在我们使用 Spring 来开发 web 应用时应该都会选择 Spring Boot 来配置 Spring,和异常相关的自动配置类为 ErrorMvcAutoConfiguration 和 WebMvcAutoConfiguration, 它们默认配置两个 HandlerExceptionResolver: DefaultErrorAttributes 和 HandleExceptionResolverComposite。

HandleExceptionResolverComposite 默认包含以下三个 HandlerExceptionResolver:

  • ExceptionHandlerExceptionResolver matches uncaught exceptions against suitable @ExceptionHandler methods on both the handler (controller) and on any controller-advices.
  • ResponseStatusExceptionResolver looks for uncaught exceptions annotated by @ResponseStatus (as described in Section 1)
  • DefaultHandlerExceptionResolver converts standard Spring exceptions and converts them to HTTP Status Codes (I have not mentioned this above as it is internal to Spring MVC).

Spring 官方博客帮我们总结了 Spring Boot 默认配置的异常处理流程:

  1. In the event of any unhanded error, Spring Boot forwards internally to /error.
  2. Boot sets up a BasicErrorController to handle any request to /error. The controller adds error information to the internal Model and returns error as the logical view name.
  3. If any view-resolver(s) are configured, they will try to use a corresponding error-view.
  4. Otherwise, a default error page is provided using a dedicated View object (making it independent of any view-resolution system you may be using).
  5. Spring Boot sets up a BeanNameViewResolver so that /error can be mapped to a View of the same name.
  6. If you look in Boot’s ErrorMvcAutoConfiguration class you will see that the defaultErrorView is returned as a bean called error. This is the View bean found by the BeanNameViewResolver.

对于 Servlet-Container 层面的错误处理,Spring 官方博客的介绍如下:

Container-Wide Exception Handling

Exceptions thrown outside the Spring Framework, such as from a servlet Filter, are also reported by Spring Boot’s fallback error page. To do this Spring Boot has to register a default error page for the container. In Servlet 2, there is an <error-page> directive that you can add to your web.xml to do this. Sadly Servlet 3 does not offer a Java API equivalent. Instead Spring Boot does the following:

  • For a Jar application, with an embedded container, it registers a default error page using Container specific API.
  • For a Spring Boot application deployed as a traditional WAR file, a Servlet Filter is used to
catch exceptions raised further down the line and handle it.

我们可以按照上述线索在 Spring Boot 的自动配置代码中找到相关的代码。

当开发 REST API 项目时,我希望业务抛出的异常能契合 Spring Boot 默认配置的异常处理机制,让整个异常体系尽量统一,接口返回给终端统一格式的错误信息,这样终端也能统一处理接口错误。那么我们应该如何做?

我们这里需要的是一个全局的异常处理机制,Spring MVC 提供给我们两种配置全局异常处理的方法:

  • 配置 HandlerExceptionResolver
  • 使用 @ControllerAdvice 注解的类

相比之下,个人觉得使用 @ControllerAdvice 注解的类会方便一些,能达到感知框架的存在。我们可以定义一个异常处理基类,发布成一个库,然后在需要用到的项目中引入这个库,在项目中继承该基类定义一个 @ControllerAdvice 注解的异常处理类。


Spring 官方博客给出了如下建议:

As usual, Spring likes to offer you choice, so what should you do? Here are some rules of thumb. However if you have a preference for XML configuration or Annotations, that’s fine too.

  • For exceptions you write, consider adding @ResponseStatus to them.
  • For all other exceptions implement an @ExceptionHandler method on a @ControllerAdvice class or use an instance of SimpleMappingExceptionResolver. You may well have SimpleMappingExceptionResolver configured for your application already, in which case it may be easier to add new exception classes to it than implement a @ControllerAdvice.
  • For Controller specific exception handling add @ExceptionHandler methods to your controller.
  • Warning: Be careful mixing too many of these options in the same application. If the same exception can be handed in more than one way, you may not get the behavior you wanted. @ExceptionHandler methods on the Controller are always selected before those on any @ControllerAdvice instance. It is undefined what order controller-advices are processed.