Java内存优化的[Key:Long,Value:Long]存储空间非常大(500M),用于并发读取访问

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:堆使用率超过了错误 我需要一些关于以下方面的指导,这有助于减少内存占用,我对访问性能的一些下降感到满意 其

我有一个用例,需要在大小为8GB的sinle VM中存储大小约为5亿条目的键值对。键和值的类型为Long。键从1、2、3开始自动递增

只有当我在程序开始时以独占操作的方式构建这个Map[K-V]结构时,一旦构建了这个Map[K-V]结构,仅用于查找,就不会在这个结构中执行更新或删除

我已经用java.util.hashMap尝试过了,但正如预期的那样,它会消耗大量内存,并且程序会给出OOM:堆使用率超过了错误

我需要一些关于以下方面的指导,这有助于减少内存占用,我对访问性能的一些下降感到满意

  • 其他替代方案是什么(来自java集合或其他库) 可以在这里试试
  • 例如,通过此映射获得内存占用的推荐方法是什么 比较目的

  • 在您的情况下,没有理由使用
    映射

    如果您只有一个开始索引,并且进一步的索引只是常量增量,那么只需使用
    列表即可:

    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[][]