当我注意到我的客户端应用程序(基于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处平稳,这对我来说似乎非常高。请记住,这只是最基本的情况,我开发的客户端应用程序已经超过400MB,这对于旧机器来说太多了。
有没有人对此行为有解释?我不明白..
(在这里使用3.1.1.RELEASE顺便说一句。)
修改* 正如axtavt所建议的那样,我还尝试直接用Test-Class作为Argument构造AnnotationConfigApplicationContext,这样就不需要类路径扫描。遗憾的是,没有改变内存消耗。
已删除编辑2 ,请参阅编辑3
编辑3 我现在使用32位和64位JVM以及上面的测试程序在同一台机器(Windows 7 64位)上进行了测试。这是结果:
基于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下测试 - &gt;同样的效果。仍然不知道为什么会发生这种情况。这对我来说真的是一个破坏者
答案 0 :(得分:7)
启动时会创建大量java.lang.reflect.Method
个对象。
这些对象符合垃圾回收的条件,但对于您的应用程序,它可能会导致过多的eden集合导致启动时间过长。
大多数java.lang.reflect.Method
个对象都在以下站点分配:
当Spring尝试在AnnotationConfigTestFrame
上查找从java.awt
和javax.swing
超类继承大量方法的setter时,似乎会创建这些set。我没有仔细阅读相关代码,但作为验证这一假设的快速测试,我做了以下内容:
@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的内存使用率相当低!
这可能会为您提供进一步调试的提示。
答案 1 :(得分:5)
构造AnnotationConfigApplicationContext
(提供带注释类的基础包)的方式需要进行类路径扫描,因此毫不奇怪它需要时间和内存。
如果要避免类路径扫描,可以尝试使用@Component
的相应构造函数来提供精确的带注释类集(@Configuration
和AnnotationConfigApplicationContext
)。 / p>