Java 减少应用程序内存占用

Java 减少应用程序内存占用,java,performance,heap-memory,Java,Performance,Heap Memory,我想存储键值对,其中键是整数,值是字符串的数组列表 我不能使用数据库,因为我必须使用代码在线解决特定比赛的问题 对于少量数据,我可以毫无问题地使用哈希表。 但是,当我的数据变大时,堆的大小就用完了。我无法更改heapsize,因为我必须只上载代码,而且我无法提供工作环境。 这就是挑战。如果无法增加堆大小,则需要限制哈希表(或使用的任何其他数据结构)的大小。我建议您尝试以下方法: LRUMap 地图的一种实现,它具有最大的大小,并使用最近使用最少的算法在需要时从地图中删除项目 已达到最大大小并添加

我想存储键值对,其中键是整数,值是
字符串的
数组列表

我不能使用数据库,因为我必须使用代码在线解决特定比赛的问题

对于少量数据,我可以毫无问题地使用哈希表。 但是,当我的数据变大时,堆的大小就用完了。我无法更改heapsize,因为我必须只上载代码,而且我无法提供工作环境。
这就是挑战。

如果无法增加堆大小,则需要限制哈希表(或使用的任何其他数据结构)的大小。我建议您尝试以下方法:

LRUMap

地图的一种实现,它具有最大的大小,并使用最近使用最少的算法在需要时从地图中删除项目 已达到最大大小并添加新项目

如果您确实需要同步版本,也可以使用:

可通过以下方式获得同步版本: Collections.SynchronizeMap(映射到同步),如果将 由多个线程访问,您必须同步对此的访问 地图。即使是并发的get(Object)操作也会产生不确定的结果 行为

如果您不想丢失使用LRU的数据,那么您需要编写一个算法,将一些数据保存在数据结构中,并保存在持久性存储中,如文件等。

一些想法

  • 如果可以写入文件,请将数据存储在那里。您可以将这些键保存在内存中的一组中,以便更快地查找,只需将值写入单个文件,甚至每个条目都可以写入一个文件

  • 创建自己的映射实现,将值列表序列化为字符串或字节[],然后压缩序列化的数据。您必须在读取时反序列化。然而,每次你做一个get/put,你都会因此在运行时受到很大的冲击。有关示例,请参见

  • 每次查找地图数据时,只要每次计算列表值,而不是存储它们——如果可以的话


  • 使用简单数组而不是
    ArrayList
    可以节省一些额外的内存(但不会太多)

    如果搜索性能不是优先级,您可以使用
    并手动执行搜索

    如果整数的范围有限,只需实例化
    列表[integer\u range]
    的数组,并使用数组索引作为键

    由于您使用的是
    字符串
    ,因此可以尝试
    intern()
    它们,并确保没有重复的值


    让我们知道关于您拥有的数据的统计信息-键是什么,值是否重复,等等。

    一种可能的优化可能是ArrayList.trimToSize,它将ArrayList使用的存储减少到最小。

    您可以将ArrayList存储为序列化(甚至可能是压缩的)。当需要访问列表时,需要对其进行反序列化、更改/读取,然后将其存储回去

    操作会慢得多,但可以进行一些缓存,将X ArrayList保留在堆中,并将其余的存储在堆外

  • 如果字符串经常重复,并且具有自然语言频率,则不要对同一字符串使用新的对象实例

    private Map<String, String> sharedStrings = new HashMap<>().
    
    public void shareString(String s) {
        String t = sharedStrings.get(s);
        if (t == null) {
            t = s;
            sharedStrings.put(t, t);
        }
        return t;
    }
    
  • 有一些map实现使用int和long等原语;例如图书馆(我自己没有使用)


  • 一张地图如何帮助我们一个哈希表是没有的,完全没有。抱歉。你能重新设计你的解决方案以使用更少的内存吗?你基本上是建议他丢弃旧数据?对我来说,这似乎根本不是一个有效的解决方案。问题是我不能从地图上删除东西,因为我正在构建这个大地图作为输入,以便对其执行操作。@NischalHp如果您不想使用LRU,数据然后你需要写一个算法,将一些数据保存在你的数据结构中,其余的数据保存在持久性存储中,如文件等。是的,我想知道我是否可以在每100个条目之后将哈希表写入一个文件,但问题是我如何只加载哈希表的一部分并读取它?一旦我将哈希表加载回文件中,堆的大小是否会超出内存?@NischalHp我认为任何数据结构都不会减少实际数据的大小。然后,您应该考虑收缩数据、arraylist大小或存储的字符串大小等。统计信息是键是整数,值是字符串的arraylist。整数的范围为1-给定输入字符串的长度,最大长度为5000个字符。值(即arraylist)的大小可以为n*n-1个元素。@nischalHp您确定需要一直存储数据吗?也许您可以动态生成所需的每个字符串?我认为你应该发布任务本身,因为如果没有它,就很难帮助你。我对所消耗的时间有限制,而且这是一场竞赛,而且在创建输入数据集后,还需要足够的时间来执行某些操作。我无法将其存储到文件中,因为我必须在线提交代码。
    int count;
    String[] allStrings = new String[999999];
    
    Map<Integer, Long> map = new HashMap<>(9999);
    
    void put(int key, List<String> strings) {
        int start = count;
        for (String s : strings) {
            allStrings[count] = s;
            ++count;
        }
        // high: start index, low: size
        long listDescriptor = (((long)start) << 32) | (count - start);
        map.put(key, listDescriptor);
    }