20

您如何优化具有大量(数百万)长寿命对象的应用程序的堆大小使用?(大缓存,从数据库加载大量记录)

  • 使用正确的数据类型
    • 避免使用 java.lang.String 来表示其他数据类型
  • 避免重复的对象
    • 如果预先知道值,则使用枚举
    • 使用对象池
    • String.intern() (好主意?)
  • 仅加载/保留您需要的对象

我正在寻找一般编程或 Java 特定的答案。没有时髦的编译器开关。

编辑:

优化可以在堆中出现数百万次的 POJO 的内存表示。

用例

  • 在内存中加载一个巨大的 csv 文件(转换为 POJO)
  • 使用休眠从数据库中检索数百万条记录

答案简历:

  • 使用享元模式
  • 写入时复制
  • 与加载具有 3 个属性的 10M 对象相比,拥有 3 个大小为 10M 的数组(或其他数据结构)是否更有效?(操作数据可能会很痛苦,但如果你真的内存不足......)
4

12 回答 12

20

我建议您使用内存分析器,查看内存消耗的位置并对其进行优化。如果没有定量信息,您最终可能会改变没有效果或实际上使事情变得更糟的事情。

您可以查看更改数据的表示形式,尤其是如果您的对象很小。例如,您可以将数据表表示为一系列列,每列都有对象数组,而不是每行一个对象。如果您不需要表示单个行,这可以为每个对象节省大量开销。例如,具有 12 列和 10,000,000 行的表可以使用 12 个对象(每列一个)而不是 1000 万个(每行一个)

于 2009-04-25T15:41:26.657 回答
18

你没有说你想要存储什么样的物品,所以提供详细的建议有点困难。然而,一些(非排他性的)方法,没有特定的顺序,是:

  • 尽可能使用享元模式。
  • 缓存到光盘。Java有 许多缓存解决方案。
  • 关于 String.intern 是否是一个好主意存在一些争论。有关问题,请参见 此处。String.intern(),以及围绕其适用性的争论。
  • 利用引用或 引用来存储可以按需重新创建/重新加载的数据。请参阅 此处了解如何将软引用与缓存技术结合使用。

了解更多有关您存储的对象的内部结构和生命周期的信息会得到更详细的答案。

于 2009-04-25T15:34:07.640 回答
11

确保对象模型的良好规范化,不要重复值。

咳咳,而且,如果只有数百万个对象,我想我会选择一个像样的 64 位 VM 和大量内存;)

于 2009-04-25T15:55:11.927 回答
4

普通的“分析器”对您没有多大帮助,因为您需要对所有“活动”对象进行概览。您需要堆转储分析器。我推荐Eclipse 内存分析器

检查重复的对象,从字符串开始。检查您是否可以应用诸如飞行重量、复制写入、延迟初始化之类的模式(谷歌将成为您的朋友)。

于 2009-04-25T21:20:17.027 回答
3

看看从这里链接的这个演示文稿。它列出了常见 java 对象和原语的内存使用情况,并帮助您了解所有额外内存的去向。

构建内存高效的 Java 应用程序:实践和挑战

于 2011-09-16T19:28:19.137 回答
2

您可以在内存中存储更少的对象。:) 使用溢出到磁盘的缓存或使用 Terracotta 来集群您的堆(这是虚拟的),允许将未使用的部分从内存中清除并以透明的方式返回。

于 2009-04-25T22:57:57.667 回答
1

我想补充一点 Peter alredy 提出的观点(不能评论他的回答 :() 使用内存分析器(检查java 内存分析器)总是比通过直觉更好。80% 的时间它是我们的例行程序忽略它有一些问题。集合类也更容易出现内存泄漏。

于 2009-04-25T18:46:43.277 回答
1

如果你有数百万个整数和浮点数等,那么看看你的算法是否允许在基元数组中表示数据。这意味着每次垃圾收集的引用更少,CPU 成本更低。

于 2009-04-29T09:20:12.687 回答
0

一个奇特的:将大多数数据压缩在 ram 中。仅扩展当前工作集。如果您的数据具有良好的局部性,则可以很好地工作。

使用更好的数据结构。Java 中的标准集合相当占用内存。

【什么是更好的数据结构】

  • 如果您查看集合的来源,您会发现如果您限制自己访问集合的方式,您可以节省每个元素的空间。
  • 集合处理增长的方式不适合大型集合。抄袭太多。对于大型集合,您需要一些基于块的算法,例如 btree。
于 2009-04-28T21:07:50.060 回答
0

花一些时间熟悉和调整VM 命令行选项,尤其是那些与垃圾收集有关的选项。虽然这不会改变对象使用的内存,但它会对具有大量 RAM 的机器上的内存密集型应用程序的性能产生重大影响。

于 2009-04-29T08:19:34.920 回答
0
  1. Assign null对所有使用的variables东西都具有价值no longer。因此make it available for Garbage collection
  2. De-reference the collections一旦使用结束,否则 GC 不会清除这些。
于 2010-04-06T06:38:49.643 回答
0

1) 尽可能使用正确的数据类型

Class Person {
 int age;
 int status;
}

在这里,我们可以在发送 Person 对象时使用以下变量来节省内存

class Person{
  short age;
  byte status;
}

2) 而不是返回 new ArrayList<>(); 从方法,你可以使用 Collection.emptyList() 它将只包含一个元素而不是默认的 10;

例如

public ArrayList getResults(){
    ..... 
    if(failedOperation)
        return new ArrayList<>();
}
//Use this
public ArrayList getResults(){
    if(failedOperation)
       return Collections.emptyList();
}

3 ) 尽可能在方法中移动对象的创建而不是静态声明,因为对象的字段将存储在堆栈而不是堆中

4) 使用 protobuf、thrift、avro、messagepack 等二进制格式来减少相互通信,而不是使用 json 或 XML

于 2019-12-23T10:53:52.023 回答