如何减少 Spring 内存占用
Posted
技术标签:
【中文标题】如何减少 Spring 内存占用【英文标题】:How to reduce Spring memory footprint 【发布时间】:2014-04-07 17:44:30 【问题描述】:我想问你如何(或如果)有可能减少 Spring 框架的 RAM 占用。
我创建了一个简单的 helloworld 应用来演示这个问题。只有两个类和 context.xml 文件:
Main
- 带有 main 方法的类
Test
- 用于模拟一些“工作”的类(printig Hello in无限循环)
context.xml
只包含这个:
<context:component-scan base-package="mypackage" />
测试类只包含名为init
的方法,在构造后调用:
@Component
public class Test
@PostConstruct
public void init()
Thread t = new Thread(new Runnable()
@Override
public void run()
try
while (true)
System.out.println("Hello " + Thread.currentThread().getName());
Thread.sleep(500);
catch (InterruptedException ex)
ex.printStackTrace();
);
t.start();
我准备了两个场景,其中main
方法只包含一行。
在第一种情况下,主要方法是这样做的:(new Test()).init();
应用程序在没有 Spring 的情况下工作,并且只消耗 aprox。 8MB 内存。
在第二种情况下,主要方法包含以下内容:new ClassPathXmlApplicationContext(new String[]"spring/context.xml");
因此应用程序通过 Spring 容器初始化并消耗 aprox。 45MB 内存!
有什么方法可以减少(最好完全摆脱)这个额外的内存?到目前为止,我无法找到任何合适的解决方案。
我不介意启动时是否有额外的内存消耗——这很好,但在那之后,我需要我们的应用程序来减少它。
(这个问题背后的故事有点复杂,但这对我来说现在是核心问题。)
谢谢
【问题讨论】:
你是如何测量内存消耗的? 如果你想使用 Spring 作为 DI 容器,我建议你尝试一些更轻量级的东西,比如 Guice 或 Dagger。 这只是一个想法,(我不知道它是否有效):我希望,当您使用基于 spring java 的配置而不是 xml 配置时,您可以减少占用空间(PermGen),因为我希望 spring 不会加载用于读取文件的 xml 库。 @injecteer 我正在使用 Windows 任务管理器,因为实际分配的内存是唯一对我重要的内存。对于其他一切,我使用 JProfiler @Ralph thx 给小费,我试试看 【参考方案1】:首先有几件事您可能已经知道但对于理解这种情况很重要:您的程序使用的大部分内存是JVM 堆。当您的程序开始执行时,堆有一个初始大小。有时 JVM 会要求操作系统提供更多内存并增加堆的大小。 JVM 也会执行垃圾回收,从而释放堆中的空间。
当使用的堆大小减少时,JVM 不一定会释放内存。 oracle JVM 不愿意这样做。例如,如果您的程序在启动时使用 500 MB 的 ram,那么在垃圾回收后大部分执行过程中只使用 100 MB,您不能假设额外的 400 MB 会返回给您的操作系统。
此外,诸如 windows 任务管理器之类的工具(我假设是 unix 的 ps
)将显示分配给 JVM 的所有内存的大小,无论该内存是否实际使用强>。 jvisualvm 之类的工具可以让您准确了解 Java 程序中内存的使用情况,尤其是您实际使用的堆数量与分配的数量。
考虑到这一点,我在以下场景中测试了您的程序。请注意,这取决于许多因素,包括您使用的 JVM、它的版本,可能还有您的操作系统。
标准 java (SE) 与 Spring。 垃圾收集 (GC) 与无 (NOGC)(我从 jvisualvm 调用了垃圾收集器)。 为 JVM 定义的最小堆大小(使用 -Xmx8M)。这告诉 JVM 在启动时只分配 8MB。我的系统默认为 256MB。对于每种情况,我都会报告分配的堆大小和使用的堆大小。这是我的结果:
SE、NOGC、256M:已分配 270 MB,已使用 30 MB Spring,NOGC,256M:已分配 270 MB,已使用 30 MB这些结果是相同的,所以根据您的问题,我假设您已经拥有与我不同的环境。
SE、GC、256M:已分配 270 MB,已使用 9 MB使用 GC 减少了堆使用量,但我们仍然拥有相同的分配内存
SE、NOGC、8M:已分配 9 MB,已使用 Spring,NOGC,8M:已分配 20 MB,已使用这是最重要的结果:分配了更多内存,因为 Spring 在启动期间可能需要更多内存。
结论:
如果您想减少堆使用量,使用 spring 应该不是什么大问题。开销并不大。 如果您试图减少分配的内存,那么在这个实验中使用 Spring 的代价会更高。但是您仍然可以配置您的 JVM,以便它比默认值更频繁地释放内存。我对此知之甚少,但诸如-XX:MaxHeapFreeRatio=70
之类的 jvm 选项可能是一个开始(更多此处为 http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html#PerformanceTuning)
【讨论】:
您好,感谢您详尽的回答。我的测量是在 Win 7、Oracle JVM 1.7 32 位上完成的。我非常了解 Java 中的这些理论记忆的东西,与 VM 选项相同(VM 调整是我们所做的第一件事)。我实际上使用 JProfiler 进行分析。我关心的唯一内存消耗是分配的数量。例如,使用 Spring 任务管理器说,它消耗 45M - 而 Jprofiler 说堆有 125M(使用了 21M - 在 SPring 的哈希图中很少使用反射字符串),没有 Spring 任务管理器说,它消耗 10M - 而 Jprofiler 说堆有 122M(已使用 7M)。 你的最后一点最适合我的问题,如果我没有遗漏什么,我会看看它。 因此,通过进一步调整 VM,我仍然有 8M 没有 Spring 和 28M(15+M 改进,但仍然太多)。根据 JProfiler 的说法,使用的堆空间量为 3M(没有 Spring 为 1,5M - 并不是真正的差异),但非堆空间为 8M(没有 Spring 为 1M)。有没有办法减少这种情况? 对不起,伙计,这个问题已经有一段时间了。请参阅我的最后评论。我不认为我有任何进展。以上是关于如何减少 Spring 内存占用的主要内容,如果未能解决你的问题,请参考以下文章
减少 Docker 和 Kubernetes 中的 JVM 内存占用