Java内存优化的[Key:Long,Value:Long]存储空间非常大(500M),用于并发读取访问
我有一个用例,需要在大小为8GB的sinle VM中存储大小约为5亿条目的键值对。键和值的类型为Long。键从1、2、3开始自动递增 只有当我在程序开始时以独占操作的方式构建这个Map[K-V]结构时,一旦构建了这个Map[K-V]结构,仅用于查找,就不会在这个结构中执行更新或删除 我已经用java.util.hashMap尝试过了,但正如预期的那样,它会消耗大量内存,并且程序会给出OOM:堆使用率超过了错误 我需要一些关于以下方面的指导,这有助于减少内存占用,我对访问性能的一些下降感到满意Java内存优化的[Key:Long,Value:Long]存储空间非常大(500M),用于并发读取访问,java,collections,guava,apache-commons,Java,Collections,Guava,Apache Commons,我有一个用例,需要在大小为8GB的sinle VM中存储大小约为5亿条目的键值对。键和值的类型为Long。键从1、2、3开始自动递增 只有当我在程序开始时以独占操作的方式构建这个Map[K-V]结构时,一旦构建了这个Map[K-V]结构,仅用于查找,就不会在这个结构中执行更新或删除 我已经用java.util.hashMap尝试过了,但正如预期的那样,它会消耗大量内存,并且程序会给出OOM:堆使用率超过了错误 我需要一些关于以下方面的指导,这有助于减少内存占用,我对访问性能的一些下降感到满意 其
在您的情况下,没有理由使用
映射
如果您只有一个开始索引,并且进一步的索引只是常量增量,那么只需使用列表即可:
List data=newarraylist(510_000_000)//理想情况下不应达到容量,如果达到容量,则需要重新分配ArrayList后面的阵列,分配的内存将增加一倍
添加数据(1337L)//插入,您希望多长时间插入一次
长值=数据。获取(1-1)//1…以1开始的索引,-1…因为索引以1开始,所以应该从索引中减去1。
如果您甚至不添加更多元素,并且从一开始就知道大小,那么数组将更好:
long[]data=long[510_000_000]//容量肯定不会达到,如果容量更大,则需要创建一个新阵列并复制所有数据
int currentIndex=0;
数据[currentIndex++]=1337L//插入,只要它小于大小
长值=数据[1-1]//1…以1开始的索引,-1…因为索引以1开始,所以应该从索引中减去1。
请注意,插入前应检查索引(currentIndex
),使其小于数组长度
迭代时,使用currentIndex+1
作为长度,而不是.length
创建一个具有所需大小的数组,并在需要访问该数组时使用arr[i-1]
(-1
,因为您的索引从1
开始,而不是从零开始)
如果“只是”有5亿个条目,则不会达到整数限制,简单数组也可以
如果您需要更多的条目并且有足够的内存,请使用数组数组
使用这么大的阵列的内存占用是数据的内存占用,甚至更多
但是,如果您不知道尺寸,您应该使用更大的长度/容量,然后您可能需要。如果使用ArrayList
,由于需要分配更大的阵列,每当达到容量时,内存占用将加倍(临时增加三倍)
一个Map
需要为每个条目创建一个对象,并为所有这些对象创建一个列表数组,这将极大地增加内存占用。内存占用的增加(使用HashMap
)甚至比使用ÀrrayList
s更糟糕,因为即使Map
没有完全填满,也会重新分配底层数组
但是如果需要存储那么多的数据,请考虑将其保存到HDD/SSD中。在大多数情况下,这种方法效果更好。您可以使用RandomAccessFile
在任何位置访问HDD/SSD上的数据。只需使用long[]
或long[][]
5亿个递增键小于2^31。如果超过2^31,请使用long[][
,其中第一个维度较小,第二个维度较大
(当密钥类型为整数时,如果密钥空间稀疏,则只需要复杂的“映射”数据结构。)
一维阵列中的空间损耗微不足道。每个Java数组节点都有12字节的头,节点大小向上舍入为8字节的倍数。因此,一个5亿个long[]
条目需要5亿个x8字节==40亿字节,这无关紧要
但是,JVM通常无法分配占用整个可用堆空间的单个对象。如果虚拟地址空间价格昂贵,建议使用二维阵列;e、 g.新长[4][125\u 000\u 000]
。这使得查找稍微复杂一些,但这样做很可能会减少内存占用
如果您事先不知道要使用的键的数量,那么可以对数组和ArrayList
对象的组合执行相同的操作。但是ArrayList
有一个问题,即如果不设置(准确的)容量,内存利用率可能会处于次优状态。如果通过追加来填充ArrayList
,则append
的瞬时内存需求可能是列表当前空间使用量的3倍。dan1st,感谢您的回复…将尝试此方法。想要更好地理解这一点,“使用这么大的数组的内存占用是数据的内存占用,还有更多。”-例如,如果有一个long[]longArray=new long[10]代码>;存储数据后的内存占用将为10*8+1=81字节。。这是正确的吗。。。?额外的字节用于存储数组开始偏移量?实际上,开销不止一个字节,因为JVM需要存储数组的长度,而数组也是对象,这会稍微增加开销,但随着大型数组的出现,这种开销正在消失。但是,一个字节的偏移量不需要存储在数组中。只需使用long[]
。5亿个递增键小于2^31。如果超过2^31,请使用long[][]