【问题标题】:Lowering memory footprint of data structure in Java降低Java中数据结构的内存占用
【发布时间】:2017-09-21 18:25:06
【问题描述】:

我正在研究一种存储数据样本的内存存储解决方案。这适用于多线程趋势应用程序,该应用程序不断将项目写入存储阵列并定期从中删除项目。它将存储最新的 24 小时样本。我需要能够全部或部分获取数据。由于这些要求,我选择使用 CopyOnWriteArrayList。

存储解决方案存储在一个

CopyOnWriteArrayList<Point>.

我编写了两个类来存放数据:Point 和 Samples。

点包括:

private int ioId;
private int machineId;
private jPointType pointType; //(int)
private String subfield;
private long startTimestamp;
private long endTimestamp;
private int pruneLock;
private jTrendReqType predefType; //(int)

CopyOnWriteArrayList<Sample> dataList;

样本包括:

Long timestamp;
double data;

我目前正在使用 2/sec 数据和 30 个点(每个点 7200 个样本)进行测试。我运行了 1 个小时的测试,通过任务管理器看到增加了大约 10MB 的使用量。这最终是每个样本大约 45 个字节。对于我存储的数据类型,这似乎相当高。有这么多 Java 开销还是我做的事情效率低?

【问题讨论】:

  • 看看Eclipse Memory Analyzer,也许它会帮助你更好地了解内存使用情况。
  • 理论上,您可以摆脱 Point 类,并将所有内容写入ByteBuffer,但这更难读取,而且读取速度可能更慢。 CopyOnWriteArrayList 对我来说似乎也有点重,因为它会在每次写入时复制列表。您说该列表是不断写入的,因此 CopyOnWrite 列表的性能会很糟糕。每个写入列表的线程都会创建并至少临时分配一个新的 ArrayList 实例。
  • @NickL 我同意它很重,但我不知道另一个具有良好可访问性以及线程安全的良好数据结构,因为读/写是从多个线程发生的。

标签: java memory-management data-structures


【解决方案1】:

好吧,让我们看看你的Sample 类:

Long timestamp;
double data;

从另一个answer 来看,Long 大约需要 16 个字节(long 需要 8 个字节和 8 个字节开销。

double 在 Java 中占用 8 个字节的内存。

Sample对象引用至少增加了8个字节(它需要存储一个long来引用内存地址)。

因此,Sample 对象本身就是 32 字节长。

但是,您计算出每个 Sample 的平均存储大小为 45 字节。

所以可能的其他原因:

  • Point 对象包含 String 大约 8 字节 + 2 字节 * 长度
  • CopyOnWriteArrayList 实现的开销
  • 未释放的内存 - 未使用但尚未被 JVM 释放的内存。

然而,最可能的原因可能是未释放的内存。由于 Java 的运行方式,只有在运行垃圾收集 (GC) 时才会释放内存(并且没有保证强制它运行的方法)。因为您使用的是 CopyOnWriteArrayList,所以您在添加对象时不断在幕后创建新列表,而 JVM 还没有释放它们,因为 GC 还没有运行。

这里有一个link 到一些关于 Java 垃圾收集机制的 Oracle 文档。

【讨论】:

  • 谢谢。我确实将 Long 更改为 long,因为它似乎不需要。
  • @Tacitus86 是的,这会有所帮助,您可以考虑调用System.gc(),它让Java 知道这是运行GC 的好时机。但是,该调用完全是建议性的,因此 JVM 可能仍然不运行 GC。最好的办法是运行更长时间,以便 JVM 运行 GC,然后检查。
  • 每个对象都有一个对象头,或者更一般地说,一个JVM特定的开销,而不仅仅是Long对象。因此,如果您假设 Long 实例的开销为 8 字节,那么您也必须假设 Sample 实例的开销为 8 字节。然后,由于这些对象由列表引用,因此每个实例都需要引用空间,如果我们假设“压缩 oops”,每个 4 个字节,所以我们已经在每个 Sample 44 个字节...
  • @Tacitus86:根据 JVM 配置,现在每个示例实例将有 24 字节或 32 字节,因为您之前的结果表明 8 字节开销(32 位或 64 位压缩哎呀和压缩的 klass 指针),你将有 8 字节标题 + 8 字节 long + 8 字节 double 并且没有进一步的填充,因为 3×8 == 24 完全可以被 8 整除。然后,需要列表中的引用,4 字节压缩 oops,没有进一步的填充,因此每个样本最终得到 28 字节。
  • 好吧,ArrayLists 的容量可能比您不指定预先计算的初始容量(并且从不修剪它们)时需要的容量大,但它不应该导致每个样本超过两个字节,鉴于其将容量增加 50% 的标准策略。
猜你喜欢
  • 1970-01-01
  • 2013-02-01
  • 2011-08-03
  • 2019-01-08
  • 1970-01-01
  • 2011-12-07
  • 2010-10-19
  • 1970-01-01
  • 2011-04-06
相关资源
最近更新 更多