代码之家  ›  专栏  ›  技术社区  ›  Ascalonian

Java内存管理的一些最佳实践是什么?[闭门]

  •  37
  • Ascalonian  · 技术社区  · 17 年前

    我从以前的开发人员那里接管了一些应用程序。当我通过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);
    }
    

    另外,在上面的第二个循环之后,我将“something”设置回null,怎么样?那会清除一些记忆吗?

    在这两种情况下,我可以遵循哪些良好的内存管理最佳实践来帮助我在应用程序中保持较低的内存使用率?

    更新:

    13 回复  |  直到 16 年前
        1
  •  37
  •   cherouvim    14 年前

    不要试图智取虚拟机。第一个循环是建议的最佳实践,包括性能和可维护性。在循环后将引用设置回null不能保证立即释放内存。当您使用尽可能小的范围时,GC将尽其最大努力。

    (从用户的角度)详细介绍这些内容的书籍包括 Effective Java 2 Implementation Patterns .

    如果您想了解更多关于性能和虚拟机内部的信息,您需要查看对话或阅读相关书籍 Brian Goetz .

        2
  •  9
  •   Community Mohan Dere    9 年前

    something ; 看见 this question

    一般最佳做法?嗯,让我们看看:除非有充分的理由,否则不要在静态变量中存储大量数据。处理完大型对象后,从集合中删除它们。哦,是的,“测量,不要猜测。”使用探查器查看内存分配的位置。

        3
  •  8
  •   Kees de Kooter chotai.mit    17 年前

    两个代码示例中都没有创建对象。只需将对象引用设置为arrayOfStuff中已有的字符串。所以记住,这没有区别。

        4
  •  5
  •   Jean Barmash    17 年前

    这两个循环将使用基本相同的内存量,任何差异都可以忽略不计。“stringsomething”只创建对一个对象的引用,而不是一个新对象本身,因此使用的任何额外内存都很小。另外,编译器/虚拟机与JVM的结合可能会优化生成的代码。

    对于内存管理实践,您应该尝试更好地分析内存,以了解瓶颈实际在哪里。尤其要注意指向大块内存的静态引用,因为它们永远不会被收集。

    最后,请记住,如果一个应用程序占用内存,可能是有原因的。。。。

    使现代化 内存管理的关键是数据结构,以及您需要/何时需要多少性能。这种折衷通常是在内存和CPU周期之间进行的。

    例如,缓存可以占用大量内存,这是为了提高性能,因为您试图避免昂贵的操作。

    因此,请仔细考虑您的数据结构,并确保您在内存中保存的时间不会超过必须的时间。如果它是一个web应用程序,请避免将大量数据存储到会话变量中,避免对大量内存池进行静态引用,等等。

        5
  •  5
  •   Mr. Shiny and New 安宇    17 年前

    最后,你必须避免做的一件事:永远不要使用终结器。终结器会干扰垃圾收集,因为对象不能被释放,但必须排队等待终结,这可能会发生,也可能不会发生。最好不要使用终结器。

    至于您在Eclipse中看到的内存使用情况,这并不一定相关。GC将根据有多少可用内存来完成它的工作。如果你有大量的可用内存,在应用程序关闭之前,你可能看不到一个GC。如果你发现你的应用程序内存不足,那么只有真正的探查器才能告诉你漏洞或效率低下的地方。

        6
  •  4
  •   Horcrux7    17 年前

    • 该程序更易于阅读。

    但从记忆的角度来看,这是不相关的。

        7
  •  4
  •   Ronald Blaschke    17 年前

    在我看来,你应该避免像这样的微观优化。它们花费了大量的大脑周期,但大部分时间几乎没有影响。

    您的应用程序可能有几个中心数据结构。这些是你应该担心的。例如,如果您将其填充,请使用对大小的良好估计预先分配它们,以避免重复调整基础结构的大小。这尤其适用于 StringBuffer , ArrayList , HashMap

    使用适当的算法访问数据结构。在最低级别,如您提到的循环,使用 Iterator s、 或者至少避免打电话 .size() 总是(是的,你每次都会问列表的大小,大多数时候都是不变的。)顺便说一句,我经常看到类似的错误 Map s人们反复浏览 keySet() get entrySet() 首先。内存管理器将感谢您提供额外的CPU周期。

        8
  •  2
  •   riz    15 年前

    正如上面的一张海报所建议的,使用探查器来测量程序某些部分的内存(和/或cpu)使用情况,而不是试图猜测它。你可能会对你的发现感到惊讶!

    我使用VisualVM进行评测,并极力推荐它。它随jdk/jre发行版一起提供。

        9
  •  1
  •   siddhadev    17 年前

    好的,第一个循环实际上更好,因为某些东西的范围更小。关于内存管理,它没有太大区别。

    大多数Java内存问题发生在将对象存储在集合中,但忘记删除它们时。否则GC会让他的工作做得很好。

        10
  •  1
  •   workmad3    17 年前

    第一个例子很好。这里没有任何内存分配,除了每次通过循环进行堆栈变量分配和释放(非常便宜和快速)。

    原因是“分配”的只是一个引用,它是一个4字节的堆栈变量(无论如何,在大多数32位系统上)。堆栈变量是通过添加到表示堆栈顶部的内存地址来“分配”的,因此非常快速且便宜。

    for (int i = 0; i < some_large_num; i++)
    {
       String something = new String();
       //do stuff with something
    }
    

        11
  •  1
  •   McDowell rahul gupta    17 年前

    如果尚未安装,我建议安装 Eclipse Test & Performance Tools Platform (TPTP)。如果要转储和检查堆,请查看SDK jmap and jhat 工具。也看到 Monitoring and Managing Java SE 6 Platform Applications .

        12
  •  0
  •   Eduard    15 年前

        13
  •  0
  •   Zeena    13 年前

    据我所知,你所看到的,底部循环并没有更好。原因是,即使您试图重用单个引用(Ex-something),事实上该对象(Ex-arrayOfStuff.get(i))仍在从列表(arrayOfStuff)引用。要使对象符合收集条件,不应从任何位置引用它们。如果确定列表在此点之后的寿命,则可以决定在单独的循环中从列表中删除/释放对象。

    可以从静态角度进行优化(即没有任何其他线程对该列表进行修改),最好避免重复调用size()。也就是说,如果你不希望大小发生变化,那么为什么要一次又一次地计算它;毕竟它不是array.length,而是list.size()。