如何改进应用程序以避免堆空间问题

时间:2012-03-02 13:14:33

标签: java memory object heap

我有一个应用程序,它与许多在方法内部创建的自定义对象有很大关系,而且从不需要在它们之外。整个结构(在我看来)是非常好的面向对象,并使用服务,实用程序和DI模型。

现在,当我运行第一次“大型”测试时,我很快遇到了OutOfMemoryExceptions。现在,我不只是想增加堆空间并完成它,因为我可以想象这不会解决问题,而是延迟它直到我的应用程序增长更多并遇到同样的问题。

我正在寻找一些简单易用的解决方案,提示和片段,它们可以帮助应用程序处理垃圾收集和堆空间,特别是涉及许多使用对象创建的循环时。

类似于“不要在循环中创建对象,在循环之前创建它们并在其中覆盖它”以及排序。

5 个答案:

答案 0 :(得分:5)

有些观点:

  • 增加堆空间没有任何根本性的错误。不同的应用程序往往有不同的要求。
  • 使用分析器查看实际情况。例如,您可以在这里找到堆分析器:MAT
  • 当您发现某些类的实例负责80%的堆消耗时:
    • 尝试找到具有相同值的共同变量集。这些候选者是一个可以由多个对象共享的对象。
    • 特别检查您是否将对相对较大的对象图的引用存储到比循环长得多的变量(局部变量消耗堆栈)。
    • 让参考文件尽快退出范围。
    • 如果您使用内部类,请检查非内部类,因为非静态内部类保持对包含对象的引用。

答案 1 :(得分:4)

我能给你的最重要的建议与任何性能问题相同:

个人资料,改进,重复

使用分析器(例如VisualVM)查找消耗最大内存量的位置。改进您的代码,首先删除任何内存泄漏,然后通常减少内存消耗。重复此过程,直到您对代码的质量和性能感到满意为止。

修改

交易的一些技巧

  • 在可能的情况下共享对象而不是重复。

  • 注意Java集合类(即各种Collection<T>Map<K,V>实现)。根据您存储的内容以及使用的集合,您可以轻松increase your memory consumption by an order of magnitude而不期待它。

  • 虽然Java(通常)没有与C中遇到的内存泄漏(通常)存在内存泄漏,但Java代码通常会遇到在其到期日期之后保持活动状态的对象的问题。

    要避免这种情况,请尽可能限制引用的范围,或者在完成该对象后将其设置为null注意:请勿使用null设置执行此操作,尤其是在预计很快会返回的简单方法中。

    最重要的是:确保从完成后可能已经输入的任何集合中删除对象。不这样做是OutOfMemoryError的良好配方 - 也是人们在Java世界中称为内存泄漏的最常见原因。

答案 2 :(得分:2)

我首先要通过使用jvisualvm(JDK的一部分)分析您的应用程序并查找内存热点。这将为您提供并指示您的对象有多大以及哪些方法调用导致高内存使用。它还会告诉你对象在内存中持续多长时间,这通常是一个很好的起点,因为你希望缩小范围尽可能短。

下一步是通过优化设计或实现缓存来识别对象中的共性。如果您正在从固定存储中加载数据,那么您可以使用软引用,这样当JVM用完堆时,这些对象将被GC(如果您对这些对象进行更改,则显然需要在删除硬引用之前保留它们)。然后,如果再次需要它们,您的应用程序将只需要从后备存储(DB,文件或其他)重新加载它们。

确保您了解GC的工作原理并理解您的对象引用:

  • 强/直接
  • 幻影

以下是一些解释参考文献和GC的好文章:

http://www.java-tips.org/java-se-tips/java.util/using-weakhashmap-for-listener-lists.html

http://pawlan.com/monica/articles/refobjs/

http://www.kdgregory.com/index.php?page=java.refobj

答案 3 :(得分:0)

一旦不再需要对象,就立即取消任何对象引用?一旦一个对象不再引用它,GC就可以收集它,但我猜你已经知道了。 GC也可以在未引用的对象图上工作(如果A只引用了B,并且没有对A的引用,则可以收集A和B)。

调用System.gc()几乎没有意义,因为如果JVM需要更多内存,它将自己完成,然后使用释放的内存。如果它无法释放更多内存,那么你会遇到OOME。

现在,默认堆大小不是那么大,因此通常可以接受更多的堆大小。

在循环中创建对象并不是特别糟糕的模式,在许多情况下它非常相关。应该避免的是在循环中重复实例化同一个对象。通常,应该在循环中避免字符串连接,并且在循环外部创建的StringBuilder替换字符串连接,因为它在性能方面效率低得多,但在内存方面却没有。

不确定我是否真的回答了你的问题。

答案 4 :(得分:0)

首先,我会重新检查我的设计,重点关注所需的高度复杂性Landau / Big O notation)。

其次,我会阅读 Josh Bloch's Effective Java, Item 6 (消除过时的对象引用),以获得一些关于

的提示
  • “内存泄漏”的常见原因
  • 而是使用可能的最小范围,然后使不再需要的对象无效
  • 缓存和存储池。

第三,如果您仍然有OOM例外,我会关注 Mikko's advises