我正在接管以前开发人员的一些应用程序.当我通过Eclipse运行应用程序时,我看到内存使用情况和堆大小增加了很多.经过进一步调查,我发现他们正在循环中创建一个对象以及其他东西.
我开始经历一些清理工作.但是我经历的越多,我就越想问"这实际上会做什么吗?"
例如,不是在上面提到的循环之外声明变量而只是在循环中设置它的值......它们在循环中创建了对象.我的意思是:
for(int i=0; i < arrayOfStuff.size(); i++) { String something = (String) arrayOfStuff.get(i); ... }
与
String something = null; for(int i=0; i < arrayOfStuff.size(); i++) { something = (String) arrayOfStuff.get(i); }
我不正确地说底部循环更好吗?也许我错了.
另外,在上面的第二个循环之后,我将"某些东西"设置为null?这会清除一些记忆吗?
在任何一种情况下,我可以遵循哪些良好的内存管理最佳实践,这将有助于在我的应用程序中保持低内存使用率?
更新:
到目前为止,我感谢每个人的反馈.但是,我并没有真正询问上面的循环(尽管根据你的建议,我确实回到了第一个循环).我正在努力获得一些我可以留意的最佳实践."当你完成使用Collection时,清除它"的界限.我真的需要确保这些应用程序不会占用太多内存.
不要试图超越VM.第一个循环是建议的最佳实践,包括性能和可维护性.在循环之后将引用设置回null将不保证立即释放内存.当您使用最小范围时,GC将发挥最佳作用.
详细介绍这些内容的书籍(从用户的角度来看)是有效的Java 2和实现模式.
如果您想了解更多关于性能和VM内部的信息,您需要查看Brian Goetz的谈话或阅读书籍.
除了范围之外,这两个循环是等价的something
; 有关详细信息,请参阅此问
一般最佳做法?嗯,让我们看看:除非你有充分的理由,否则不要在静态变量中存储大量数据.完成后,从集合中删除大对象.哦,是的,"措施,不要猜." 使用分析器查看内存的分配位置.
两个代码示例中都没有创建任何对象.您只需将对象引用设置为arrayOfStuff中已有的字符串.所以记忆中没有区别.
这两个循环将使用基本相同的内存量,任何差异都可以忽略不计."String something"仅创建对象的引用,而不是自身的新对象,因此使用的任何额外内存都很小.此外,编译器/与JVM结合可能会优化生成的代码.
对于内存管理实践,您应该尝试更好地分析内存,以了解实际存在的瓶颈.特别注意指向大块内存的静态引用,因为它永远不会被收集.
您还可以查看弱引用和其他专用内存管理类.
最后,请记住,如果应用程序占用内存,可能有一个原因....
更新内存管理的关键是数据结构,以及您需要/何时的性能.权衡通常在内存和CPU周期之间进行.
例如,缓存可以占用大量内存,特别是为了提高性能,因为您试图避免昂贵的操作.
因此,请仔细考虑您的数据结构,并确保您不会将内容保留在内存中的时间超过必须的时间.如果它是一个Web应用程序,请避免将大量数据存储到会话变量中,避免对大量内存池进行静态引用等.
JVM最擅长释放短期对象.尽量不要分配不需要的对象.但在了解工作负载,对象生存期和对象大小之前,无法优化内存使用情况.分析器可以告诉你这个.
最后,你必须避免做的第一件事:永远不要使用终结器.终结器会干扰垃圾收集,因为对象不能只是被释放,而是必须排队等待最终确定,这可能会也可能不会发生.最好不要使用终结器.
至于你在Eclipse中看到的内存使用情况,它并不一定相关.GC将根据可用内存量来完成其工作.如果您有大量可用内存,则在关闭应用程序之前可能看不到单个GC.如果您发现您的应用程序内存不足,那么只有真正的分析器可以告诉您泄漏或效率低下的位置.