如何减少 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 内存占用的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio 2010——如何减少其内存占用

IM即时通讯开发如何减少Bitmap内存占用?

减少 Docker 和 Kubernetes 中的 JVM 内存占用

Spring Boot 嵌入式 tomcat 服务器占用 800 MB 内存?

减少 Mongrel Rails 内存占用并提高性能?

程序丨如何把WebGL显存占用减少84.2%?