【问题标题】:Large number of Object in Java (with HashMap)Java中的大量对象(带有HashMap)
【发布时间】:2015-05-24 19:22:32
【问题描述】:

你好,

我目前正在使用 Java 进行单词预测。 为此,我使用的是基于 NGram 的模型,但我有一些内存问题...

我第一次有这样的模型:

public class NGram implements Serializable {
    private static final long serialVersionUID = 1L;

    private transient int count;
    private int id;
    private NGram next;

    public NGram(int idP) {
        this.id = idP;
    }
}

但这需要很多内存,所以我认为我需要优化,我想如果我有“hello the world”和“hello the people”,而不是得到两个 ngram,我可以保留一个保留“Hello the”然后有两种可能:“people”和“world”。

更清楚地说,这是我的新模型:

public class BNGram implements Serializable {
    private static final long serialVersionUID = 1L;
    private int id;
    private HashMap<Integer,BNGram> next;
    private int count = 1;

    public BNGram(int idP) {
        this.id = idP;
        this.next = new HashMap<Integer, BNGram>();
    }
}

但似乎我的第二个模型消耗了两倍的内存......我认为这是因为 HashMap,但我不知道如何减少这个?我尝试使用不同的 Map 实现,例如 Trove 或其他,但它并没有改变任何东西。

给你一个想法,对于一个 9MB 的文本,有 57818 个不同的单词(不同,但不是单词的总数),在 NGram 生成之后,我的 javaw 进程消耗了 1.2GB 的内存...... 如果我用 GZIPOutputStream 保存它,它会占用大约 18MB 的磁盘空间。

所以我的问题是:我怎样才能使用更少的内存?我可以用压缩(作为序列化)制作一些东西吗? 我需要将此添加到其他应用程序中,因此我需要减少内存使用之前...

非常感谢,抱歉我的英语不好......

ZiMath

【问题讨论】:

  • 您是否尝试分析您的内存消耗?例如。使用 jvisualvm?
  • 我目前正在使用 Java 任务控制来获取有关内存消耗的更好信息。
  • 我不熟悉 n-gram 问题,链表(您的第一个解决方案)是推荐的方法吗?
  • 每个对象至少消耗 32 个字节,可能是 48 个字节,具体取决于 JVM。字符串实际上是两个对象(当然,字符也会占用空间)。 HashMap 条目是另一个对象。对于像您这样的应用程序,减少内存消耗的最佳方法是使用数据库来保存大量数据。
  • LinkedList 不是推荐的方法,因为它降低了预测的性能(HashMap 很快,因为 get(...) 操作很快,所以 NGram 与 HashMap 在时间上非常有效)。我知道每个对象都会消耗内存,这就是为什么我不存储单词(在字符串中)而是在 int 中。您对嵌入式数据库有想法吗?我尝试了 H2,但它也消耗了大量内存!

标签: java performance memory hashmap


【解决方案1】:

你需要一个专门的结构来实现你想要的。

看看Apache's PatriciaTrie。它就像Map,但它是内存方面的,并且可以与Strings 一起使用。它也非常快:操作是O(k)k 是最大密钥的位数。

它有一个适合您即时需求的操作:prefixMap(),它返回包含 Strings 的特里树的 SortedMap 视图,该树以给定键为前缀。

一个简短的使用示例:

public class Patricia {

    public static void main(String[] args) {

        PatriciaTrie<String> trie = new PatriciaTrie<>();

        String world = "hello the world";
        String people = "hello the people";

        trie.put(world, null);
        trie.put(people, null);

        SortedMap<String, String> map1 = trie.prefixMap("hello");
        System.out.println(map1.keySet());  // [hello the people, hello the world]

        SortedMap<String, String> map2 = trie.prefixMap("hello the w");
        System.out.println(map2.keySet()); // [hello the world]

        SortedMap<String, String> map3 = trie.prefixMap("hello the p");
        System.out.println(map3.keySet());  // [hello the people]
    }
}

还有the tests,里面有更多例子。

【讨论】:

  • 非常感谢,我会试试,然后我会发布结果。在您回答之前,我尝试了一个 TIntObjectHashMap(来自 Trove),并根据需要创建了一个 Map(地图不是在构造函数中创建,而是仅在添加一些元素时创建),我得到了 600MB 的使用量。我们将看看 PatriciaTrie 是否可以做得更好!谢谢
  • @zimath 我不知道哪个消耗的内存更少。当你有比较结果时请告诉我。 PatriciaTrie 具有运营优势。我将在我的答案中添加一个示例。
  • 谢谢@Magnamag,这是一个不错的尝试,但是通过仅使用 String 的简单测试(并且没有任何 supp 信息作为我的模型中的计数),我的内存使用量为 900MB,所以它不符合我的要求。当我想到我的第二个解决方案时,它是一种 PatriciaTrie。再次感谢;)
  • 我会尝试,但我不确定这是否会节省大量空间,因为在 Java 中,String 被缓存,所以“hello”和“hello”,即使一个键和另一个在值上,将“指向”同一个 String 对象。
  • @zimath Q 关于大约 60k 个不同单词的 9mb 文本。你介意解释一下里面有什么吗?你应该如何根据该文件的信息来预测单词?只是为了获得更多背景信息并更好地理解您的问题。
【解决方案2】:

在这里,我主要是想解释为什么您观察到如此过多的内存消耗,以及您可以对此做些什么(如果您想坚持HashMap) :

使用默认构造函数创建的HashMap初始容量为 16。这意味着它将有空间容纳 16 个条目,即使它是空的。此外,无论是否需要,您似乎都在创建地图。

所以在你的情况下减少内存消耗的方法是

  • 仅在必要时创建地图
  • 使用较小的初始容量创建它

应用于您的课程,大致如下所示:

public class BNGram {
    private int id;
    private Map<Integer,BNGram> next;

    public BNGram(int idP) {
        this.id = idP;
        // (Do not create a new `Map` here!)
    }

    void doSomethingWhereTheMapIsNeeded(Integer key, BNGram value) {

        // Create a map, when required, with an initial capacity of 1
        if (next == null) {
            next = new HashMap<Integer, BNGram>(1);
        }
        next.put(key, value);
    }
}

但是……

...从概念上讲,拥有一个由许多许多映射组成的大型“树”结构是有问题的,每个映射只有“很少”条目。这表明不同的数据结构在这里更合适。因此,您绝对应该更喜欢answer by Magnamag 中的解决方案,或者(如果这对您不适用,如您的 cmets 中所建议的那样),请寻找替代数据结构 - 甚至可以将其作为一个新问题提出不会受到XY Problem 的影响。

【讨论】:

  • 我已经解决了地图创建问题(如我上一条评论中所说),我节省了大约 300MB(如果我的 BNGram 中只有一个孩子,则使用缓存)。但你是对的,可能我必须搜索不同的数据结构......谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-12
  • 1970-01-01
  • 1970-01-01
  • 2016-02-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多