减少应用程序的内存占用

3
我希望存储键值对,其中键是整数,值是字符串的ArrayLists。
我不能使用数据库,因为我必须使用代码来解决特定比赛的在线问题。
对于少量数据,我可以在没有任何问题的情况下使用哈希表。但是当我的数据变得很大时,我会耗尽堆大小。我无法更改堆大小,因为我只能上传代码,而且我不能提供工作环境。这是挑战。

3
如果散列表不起作用,地图会如何帮助? - Juned Ahsan
完全错过了那个。抱歉。 - Andrew Martin
你能否重新设计你的解决方案,以使用更少的内存? - user902383
6个回答

3
  1. If the strings are repeated often, have natural language frequences, do not use new object instances for the same string.

    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;
    }
    
  2. A numbering of the strings probably is too slow.

  3. Packing the list of strings in a single one (separator some control character), and possibly Gzipping the String (GZipOutputStream, GZipInputStream).

  4. Tune the hash map with a sufficient initial capacity. (Sorry if I state the obvious.)

  5. Do your own allocation of all ArrayLists, using huge large String[]:

    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);
    }
    
  6. There are map implementations using primitives like int and long; the trove library for instance (did not use it myself).


1

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

如果搜索性能不是优先考虑因素,可以使用一个 Pair<Integer, List<>> 并手动进行搜索。

如果整数范围有限,只需实例化一个 List[integer_range] 的数组并使用数组索引作为键。

由于您正在使用 Strings,可以尝试对它们进行 intern() 处理,并确保没有重复值。

请告诉我们关于数据的统计信息 - 键是什么,值是否重复等等。


统计信息是键为整数,值为字符串数组列表。 整数的范围可以从1到给定输入字符串的长度,最大可达5000个字符。 值即数组列表可以有n*n-1个元素的大小。 - Nischal Hp
@nischalHp,你确定需要一直存储数据吗?也许你可以动态生成所需的每个字符串?我认为你应该发布任务本身,因为没有它将很难帮助你。 - Dariusz

0

一些想法

  1. 如果可以写入文件,就将数据存储在那里。你可以在内存中保留键的集合以加快查找速度,然后只写出值——可以是单个文件,也可以是每个条目一个文件。

  2. 创建自己的映射实现,将值列表序列化为字符串或byte[],然后压缩序列化数据。但每次进行get/put操作时都会导致大量运行时开销。请参见 http://theplateisbad.blogspot.co.uk/2011/04/java-in-memory-compression.html 以获取示例。

  3. 每次查找映射数据时,仅计算列表值而不存储它们——如果可以的话。


我有时间限制,而且这是一个比赛,我创建输入数据集后还需要执行某些操作,所需的时间充足。我不能将其存储到文件中,因为我必须在线提交代码。 - Nischal Hp

0

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


0

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

操作会显著变慢,但您可以做一些缓存以保留X个ArrayList在堆中,并在外部存储其余部分。


-1

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

LRUMap

这是一个 Map 的实现,它具有最大大小,并使用最近最少使用算法从 Map 中删除项目,当达到最大大小并添加新项目时。

如果您确实需要同步版本,则也可以使用以下方式获得:

可以通过以下方式获得同步版本:Collections.synchronizedMap( theMapToSynchronize ) 如果将被多个线程访问,则必须同步访问此 Map。即使是并发的 get(Object) 操作也会产生不确定的行为。

如果您不想失去使用 LRU 数据,则需要编写算法将一些数据保留在数据结构中,其余数据存储在持久性存储中,例如文件等。


你基本上是在建议他丢弃旧数据?这对我来说根本不是一个有效的解决方案。 - Xabster
问题是我不能从地图中删除任何东西,因为我正在构建这个大地图作为输入来执行操作。 - Nischal Hp
@NischalHp 如果你不想失去使用LRU的数据,那么你需要编写一个算法来保留一些数据在你的数据结构中,而将其余部分存储在持久化存储器中,例如文件等。 - Juned Ahsan
是的,我正在尝试每输入100个条目后将我的哈希表写入文件,但问题是如何只加载哈希表的部分并读取它?如果我将哈希表从文件中加载回来,堆大小不会超出内存吗? - Nischal Hp
另外一件事是我不认为我可以使用文件,因为我只需要上传代码并进行验证,我不认为我会被授予写入文件的权限,因为它是在线的。我能否使用数据结构来管理内存而不创建文件? - Nischal Hp
2
@NischalHp 我认为没有任何数据结构可以减小实际数据的大小。那么你应该考虑缩小数据、ArrayList 大小或存储的字符串大小等。 - Juned Ahsan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接