OpenHFT ChronicleMap 内存分配和限制

Fel*_*lix 3 java memory chronicle-map

这篇文章很可能是 OpenHFT 常见问题的一个很好的候选人。

我正在玩 ChronicleMap 考虑它的一个想法,但有很多问题。我相信大多数正在研究这个产品的初级程序员都有类似的考虑。

你能解释一下这个 API 是如何管理内存的吗?

ChronicleMap 宣布了一些显着的 TB 堆外内存资源可用于处理其数据,我想对此有一个清晰的认识。

让我们来看看一个程序员,它有一台 500GB HD 和 4GB RAM 的笔记本电脑。在这种情况下,纯数学 sais - 可用“交换”内存的总资源为 504GB。让我们给操作系统和其他程序一半,我们只剩下 250GB 硬盘和 2GB 内存。您能否详细说明 ChronicleMap 相对于可用资源可以分配的实际可用内存数量?

接下来的相关问题是关于 ChronicleMap 的实现。

我的理解是,每个 ChronicleMap 分配它使用的内存块,当我们可以准确预测通过的数据量时,实现最佳性能/内存使用。然而,这是一个动态的世界。

让我们设置一个(夸张但可能的)示例:

假设 K(关键)“城市”及其 V(值)-“描述”(城市)的地图,并允许用户对描述长度有很大限制。

第一个用户输入: K = "Amsterdam"V = "City of bicycles"此条目用于声明映射 - 它为该对设置了先例,如下所示:

ChronicleMap<Integer, PostalCodeRange> cityPostalCodes = ChronicleMap
    .of(CharSequence.class, CharSequence.class)
    .averageKey("Amsterdam")
    .averageValue("City of bicycles")
    .entries(5_000)
    .createOrRecoverPersistedTo(citiesAndDescriptions);
Run Code Online (Sandbox Code Playgroud)

现在,下一个用户被带走并写了一篇关于布拉格的分析他传递给:K = "Prague"V = "City of 100 towers is located in the hard of Europe ... blah, blah... million words ..."

现在程序员原本预计最多有 5_000 个条目,但它超出了他的控制,有成千上万的条目。

ChronicleMap 是否会为这种情况自动分配内存?如果是,是否有更好的方法来为这个动态解决方案声明 ChronicleMaps?如果不是,您会推荐一种方法(在代码示例中最好)如何最好地处理这种情况?

这如何与持久性文件一起工作?

ChronicleMaps 会耗尽我的 RAM 和/或磁盘空间吗?避免这种情况的最佳实践?

换句话说,请解释在低估和高估值(和/或键)长度和条目数的情况下如何管理内存。

其中哪些适用于 ChronicleMap?

  1. 如果我分配大块 ( .entries(1_000_000).averageValueSize(1_000_000)并且实际使用量是 - 条目 = 100,并且平均值大小 = 100。

发生什么了?:

1.1. - 一切正常,但会有大块浪费 - 未使用?

1.2. - 一切正常,未使用的内存可用于:

1.2.1 - 编年史地图

1.2.2 - 使用 ChronicleMap 的给定线程

1.2.3 - 给定过程

1.2.4 - 给定 JVM

1.2.5 - 操作系统

1.3. - 请解释未使用的内存是否发生其他情况

1.4. - 过大的声明对我的持久性文件有什么影响?

  1. 与情况 1 相反 - 我分配了小块 ( .entries(10).averageValueSize(10)实际使用量为 1_000_000s 个条目,而平均值大小 = 1_000s 个字节。会发生什么?:

lev*_*tov 5

让我们来看看一个程序员,它有一台 500GB HD 和 4GB RAM 的笔记本电脑。在这种情况下,纯数学 sais - 可用“交换”内存的总资源为 504GB。让我们给操作系统和其他程序一半,我们只剩下 250GB 硬盘和 2GB 内存。您能否详细说明 ChronicleMap 相对于可用资源可以分配的实际可用内存数量?

在这种情况下,Chronicle Map 会非常慢,每次使用 Chronicle Map 的操作平均有 2 次随机磁盘读写(总共 4 次随机磁盘操作)。当数据库大小远大于内存时,传统的基于磁盘的数据库引擎,如RocksDBLevelDB,应该能更好地工作。


现在程序员原本预计最多有 5_000 个条目,但它超出了他的控制,有成千上万的条目。

ChronicleMap 是否会为这种情况自动分配内存?如果是,是否有更好的方法来为这个动态解决方案声明 ChronicleMaps?如果不是,您会推荐一种方法(在代码示例中最好)如何最好地处理这种情况?

Chronicle Map 会分配内存,直到实际插入的条目数除以配置的数量ChronicleMapBuilder.entries()不大于配置的数量ChronicleMapBuilder.maxBloatFactor()。例如 如果您将地图创建为

ChronicleMap<Integer, PostalCodeRange> cityPostalCodes = ChronicleMap
    .of(CharSequence.class, CharSequence.class)
    .averageKey("Amsterdam")
    .averageValue("City of bicycles")
    .entries(5_000)
    .maxBloatFactor(5.0)
    .createOrRecoverPersistedTo(citiesAndDescriptions);
Run Code Online (Sandbox Code Playgroud)

IllegalStateException当大小为 ~ 25 000 时,它将开始尝试插入新条目。

然而,当实际大小远远超过配置的大小时,Chronicle Map 的工作速度会逐渐变慢,因此最大可能maxBloatFactor()被人为地限制为 1000。

现在的解决方案是通过entries()(和averageKey()、 和averageValue())至少大致正确地配置 Chronicle Map 的未来大小。

预先配置合理的 Chronicle Map 大小的要求被认为是一个可用性问题。有一种方法可以解决这个问题,它在项目路线图上。


换句话说,请解释在低估和高估值(和/或键)长度和条目数的情况下如何管理内存。

键/值大小低估:空间浪费在哈希查找区域,每个条目约 8 字节 * 低估因子。因此,如果实际平均条目大小(键 + 值)很小,例如 50 字节,并且您将其配置为 20 字节,则可能会非常糟糕,您将浪费 ~ 8 * 50 / 20 = 20 字节,或 40%。平均条目大小越大,浪费越小。

键/值大小高估:如果你只配置键和值的平均大小,而不是actualChunkSize()直接配置,实际的块大小会自动在平均条目大小(键+值)的 1/8 和 1/4 之间选择。实际的块大小是 Chronicle Map 中的分配单位。因此,如果您将平均条目大小配置为 ~ 1000 字节,则实际块大小将在 125 到 250 字节之间选择。如果实际平均条目大小仅为 100 字节,您将损失大量空间。如果高估很小,则预期的空间损失将限制在数据大小的 20% 左右。

因此,如果您担心可能会高估平均键/值大小,请actualChunkSize()明确配置。

条目数低估:上面讨论过。没有特别的空间浪费,但 Chronicle Map 的工作速度较慢,低估越严重。

条目数高估:内存浪费在哈希查找区域,每个条目约 8 字节 * 高估因子。请参阅上面的键/值大小低估部分,了解它的好坏程度,具体取决于实际的平均条目数据大小。