当 JVM 空闲时将内存释放回 OS

Releasing memory back to OS when JVM is idle

我们有一个简单的微服务设置,基于 Spring Boot 和 Windows 服务器上的 Java 8。

许多服务的负载很低,因为它们是对各种外部合作伙伴的集成。所以很多时候他们都是闲着的。

问题在于 JVM 仅在触发垃圾回收时将内存释放回 OS。因此,服务可能开始使用 32mb,然后服务于单个请求并分配 2GB 内存。如果该服务上没有其他 activity,则它不会 GC 并且服务器上的其他服务将受到影响。

使用 System.gc 在外部或内部触发 GC 效果很好,我已经弄清楚如何使用 -XX:MaxHeapFreeRatio-XX:MinHeapFreeRatio 以及 -XX:+UseG1GC 来控制何时堆应该扩展并释放内存到 OS.

我的问题是:当 JVM 空闲时确保内存释放回 OS 的最佳方法是什么?

一个想法是让服务监控自身并在空闲一段时间后触发 System.gc,但这可能很棘手且容易出错。所以希望有更好的建议。

您可以复制此程序的 运行 X 个实例。大约 10 个使我的 Windows 8GB 机器放弃。

import java.util.*;

public class Load {
  public static void main(String[] args) throws Exception {
    alloc();
    Scanner s = new Scanner(System.in);
    System.out.println("enter to gc ");
    s.nextLine();
    System.gc();
    System.out.println("enter to exit");
    s.nextLine();
  }

  private static void alloc() {
    ArrayList<String[]> strings = new ArrayList<>();
    int max = 1000000;
    for (int i = 0; i < max; i++) {
      strings.add(new String[500]);
    }
  }
}

c:\> java -server -XX:+UseG1GC -Xms32m -Xmx2048m Load

编辑:这被标记为重复两次,但它不是链接问题的重复。第一个问题是同一个问题的 2010 版本,但该问题是关于为什么 GC 不将内存释放回 OS(这在当时是不可能的)。另一个问题是关于基本的 GC 设置,我已经写过我理解的内容。我希望讨论如何在系统空闲时触发垃圾收集器。因此,每五秒 运行 System.gc 是不可接受的,因为这很可能会与有效请求发生冲突并破坏响应时间。

如果调用 System.gc() 可以满足您的需求,我建议使用 spring 调度程序来 运行 每 x 秒执行一次周期性任务。

这个实现起来还是挺容易的,一些注解

@EnableAsync
@EnableScheduling
@Scheduled(cron = "...")

就是你所需要的。 有关详细信息,请参阅 spring scheduling

编辑

调用 System.gc() 仅给出启动垃圾收集的建议,仍由 JVM 决定何时执行或不执行。

要了解您的系统是否空闲,您可以使用 spring 指标。

中有一些子 类
org.springframework.boot.actuate.endpoint.PublicMetrics

比如 TomcatPublicMetrics 或 SystemPublicMetrics,它们可以为您提供有关系统的信息。 您可以使用 @Autowire 注入它们并调用 mertics() 来获取单个值。基于此,您也许可以决定您的系统是否空闲,