【问题标题】:spring annotation-based configuration - memory consumption too high?spring 基于注解的配置——内存消耗太高?
【发布时间】:2012-07-18 07:40:59
【问题描述】:

当我注意到我的客户端应用程序(基于 Swing 的)上的 RAM 使用率非常高时,我开始研究它,这似乎与 Spring 中基于 Annotation 的配置有关。正如您将在下面的编辑中看到的那样,我意识到这只发生在 64 位 JVM 上。

查看以下测试代码:

基于xml的配置

<beans ....>
     <bean id="xmlConfigTest" class="at.test.XmlConfigTest" />
</beans>

public class XmlConfigTest extends JFrame {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("config/applicationContext.xml");
        XmlConfigTest frame = (XmlConfigTest) ctx.getBean("xmlConfigTest");
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

使用大约 32MB 内存,我觉得还可以。

现在与基于注释的配置相同:

@Service
public class AnnotationConfigTestFrame extends JFrame {
    public static void main(String[] args) throws InterruptedException {
        ApplicationContext ctx = new AnnotationConfigApplicationContext("at.test");

        AnnotationConfigTestFrame frame = (AnnotationConfigTestFrame) ctx
            .getBean("annotationConfigTestFrame");
       frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
       frame.setVisible(true);
    }
}

不仅打开框架需要更长的时间,而且内存消耗在启动时飙升至 160MB 内存,然后稳定在 152MB 左右,这对我来说似乎真的很高。请记住,这只是最基本的情况,我在 atm 开发的客户端应用程序已经占用了超过 400MB,这对于旧机器来说太多了。

有人对这种行为有解释吗?没看懂。。

(顺便说一句,这里使用 3.1.1.RELEASE。)

编辑* 正如 axtavt 所建议的那样,我还尝试使用 Test-Class 作为参数直接构造 AnnotationConfigApplicationContext,这样就不需要进行类路径扫描。不幸的是,内存消耗没有任何改变。

编辑 2 已移除,请参阅编辑 3

编辑 3 我现在在同一台机器(Windows 7 64 位)上测试了 32 位和 64 位 JVM 以及上面的测试程序。结果如下:

基于xml的配置:

32-Bit JVM: 16MB
64-Bit JVM: 31MB

基于注释的配置:

32-Bit JVM: 17MB
64-Bit JVM: 160MB

所以在 32 位 JVM 上,两个程序都很接近,这几乎是我所期望的。但在 64 位上,这是不同的。即使是第一个程序在 64 位上使用两倍的内存,这似乎已经太多了。仍然对第二个程序没有任何影响,它在 64 位上使用了近 10 倍的内存。

编辑 4 现在也在 ubuntu 下测试了 -> 同样的效果。仍然不知道为什么会这样。这对我来说真的是一个交易破坏者

【问题讨论】:

  • 你能确定你使用的是哪个JVM和JVM版本吗?
  • 尝试使用 (oracle) 1.6.0_24 和 (orcale) 1.7.0_03。不幸的是,几乎没有区别
  • 我的一个建议是在 32 位和 64 位 JVM 上进行堆转储(在启动后立即)。比较在两个实例中创建的对象的数量,并验证是否在两种情况下都创建了相同数量的相同类型的对象。另一个建议是在 64 位 JVM 上使用 CompressedOops 标志运行应用程序。
  • @Matrium 我无法在 Mac OS X 的 1.6.0_33 64 位 JVM 上使用 Spring 3.1.2.RELEASE 重现高内存消耗。在我的机器上,XmlConfigTest 占用了 15M,而 AnnotationConfigTestFrame 仅占用了 5M。您在类路径或 at.test 包中有很多类吗?
  • @Matrium 我收回这一点 - 我可以看到 Linux 和 OS X 上的大量内存使用情况。请参阅 pastie.org/4307910pastie.org/4307915

标签: java spring jvm spring-annotations


【解决方案1】:

构建AnnotationConfigApplicationContext(提供带注释的类的基本包)的方式需要扫描类路径,因此它需要时间和内存也就不足为奇了。

如果您想避免类路径扫描,您可以尝试提供精确的注释类集(@Components 和 @Configurations),使用 AnnotationConfigApplicationContext 的相应构造函数。

【讨论】:

  • 我只是试了一下,然后这样构造它:new AnnotationConfigApplicationContext(AnnotationConfigTestFrame.class); 它有效,并没有改变内存消耗的想法
【解决方案2】:

在启动时会创建大量java.lang.reflect.Method 对象。

这些对象有资格进行垃圾回收,但对于您的应用程序,它可能会导致过多的伊甸园回收,从而导致启动时间过长。

大部分java.lang.reflect.Method 对象分配在以下站点:

这些似乎是当 Spring 尝试在 AnnotationConfigTestFrame 上查找 setter 时创建的,它从 java.awtjavax.swing 超类继承了许多方法。我没有仔细阅读相关代码,但作为验证这个假设的快速测试,我做了以下操作:

@Service
public class AnnotationConfigTestFrame /* extends JFrame */
{
    public static void main(String[] args) throws InterruptedException
    {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AnnotationConfigTestFrame.class);

        AnnotationConfigTestFrame frame = (AnnotationConfigTestFrame) ctx
                .getBean("annotationConfigTestFrame");
//        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
//        frame.setVisible(true);

        waitABit();
        printRuntimeStats();
        System.exit(0);
    }
}

即使AnnotationConfigTestFrame 不继承自javax.swing.JFrame。现在查找 bean 的内存使用量相当低!

这可能会为您提供进一步调试的提示。

【讨论】:

  • 这真的很有帮助,谢谢!我想我将从现在开始为我的 gui 包使用 xml-configuration 而不是扫描。
猜你喜欢
  • 2019-01-16
  • 2020-01-06
  • 1970-01-01
  • 2021-07-10
  • 2018-02-13
  • 2020-03-19
  • 2020-04-16
  • 2011-09-23
  • 1970-01-01
相关资源
最近更新 更多