java内存优化[key:long, value:long]用于并发读访问的超大容量(500米)存储器

mdfafbf1  于 2021-06-30  发布在  Java
关注(0)|答案(2)|浏览(358)

我有一个用例,需要在8GB大小的sinleVM中存储大小约为5亿个条目的键值对。键和值的类型为long。键从1、2、3开始自动递增。。
只有当我在程序开始时构建这个map[k-v]结构作为一个独占操作时,一旦构建了这个map[k-v]结构,只用于查找,就不会在这个结构中执行更新或删除。
我用java.util.hashmap尝试过这个方法,但正如预期的那样,它会消耗大量内存,并且程序会给出oom:heap用法超过错误。
我需要一些关于以下的指导,这有助于减少内存占用,我可以在访问性能下降一些。
在这里可以尝试的其他替代方法(来自java集合或其他库)是什么。
为便于比较,建议使用什么方法通过此Map获取内存占用。

vmdwslir

vmdwslir1#

没有理由使用 Map 对你来说。
如果你只是有一个开始索引,而进一步的索引只是常数增量,那么就使用 List :

List<Long> data=new ArrayList<>(510_000_000);//capacity should ideally not be reached, if it is reached, the array behind the ArrayList needs to be reallocated, the allocated memory would be doubled by that

data.add(1337L);//inserting, how often you want

long value=data.get(1-1);//1...your index that starts with 1, -1...because your index starts with 1, you should subtract one from the index.

如果您甚至不添加更多元素,并且从一开始就知道大小,那么数组会更好:

long[] data=long[510_000_000];//capacity should surely not be reached, you will need to create a new array and copy all data if it is higher
int currentIndex=0;

data[currentIndex++]=1337L//inserting, as often as it is smaller than the size

long value=data[1-1];//1...your index that starts with 1, -1...because your index starts with 1, you should subtract one from the index.

注意,您应该检查索引( currentIndex )在插入之前,使其小于数组长度。
迭代时,使用 currentIndex+1 作为长度而不是 .length .
创建一个具有所需大小的数组,并在需要访问它时使用 arr[i-1] ( -1 因为你的作品是从 1 而不是零)。
如果“just”有5亿个条目,就不会达到整数限制,一个简单的数组就可以了。
如果需要更多的条目并且内存充足,请使用数组。
使用这么大的数组的内存占用是数据的内存占用,还有更多。
但是,如果您不知道尺寸,您应该使用更高的长度/容量,然后您可能需要。如果你使用 ArrayList ,每当达到容量时,内存占用就会加倍(暂时加倍),因为它需要分配一个更大的数组。
Map 每个条目都需要一个对象,所有这些对象都需要一个列表数组,这样会大大增加内存占用。内存占用的增加(使用 HashMap )更糟糕的是 ÀrrayList s作为基线阵列进行重新分配,即使 Map 没有完全填满。
但是,如果需要存储那么多数据,可以考虑将其保存到hdd/ssd。在大多数情况下,这种方法效果更好。你可以用 RandomAccessFile 以便在任何位置访问hdd/ssd上的数据。

5ssjco0h

5ssjco0h2#

就用一个 long[] 或者 long[][] .
5亿个升序键小于2^31。如果你超过2^31,用 long[][] 其中第一个维度很小,第二个维度很大。
(当密钥类型为整数时,如果密钥空间稀疏,则只需要一个复杂的“map”数据结构。)
一维阵列的空间损耗是微不足道的。每个java数组节点都有12字节的头,节点大小向上舍入为8字节的倍数。所以有5亿人 long[] 需要5亿x 8字节==40亿字节,这无关紧要。
但是,jvm通常不能分配占用整个可用堆空间的单个对象。如果虚拟地址空间是溢价,这将是明智的使用2-d数组;例如 new long[4][125_000_000] . 这使得查找稍微复杂一些,但是这样做很可能会减少内存占用。
如果您事先不知道预期的键数,那么可以使用数组和 ArrayList 物体。但是一个 ArrayList 有一个问题,如果你不设置一个(准确的)容量,内存利用率很可能是次优的。如果你在 ArrayList 通过附加到它,对 append 最多可以是列表当前空间使用量的3倍。

相关问题