当 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() 来获取单个值。基于此,您也许可以决定您的系统是否空闲,
我们有一个简单的微服务设置,基于 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() 来获取单个值。基于此,您也许可以决定您的系统是否空闲,