【问题标题】:Serializable, cloneable and memory use in JavaJava中的可序列化、可克隆和内存使用
【发布时间】:2011-08-08 21:18:05
【问题描述】:

我正在使用一个内部类,它是 HashMap 的子类。我有一个 String 作为键和 double[] 作为值。我每个 double[] 存储大约 200 个双精度数。我应该使用大约 700 MB 来存储键、指针和双精度数。但是,内存分析表明我需要的远不止这些(略高于 2 GB)。

使用TIJmp(分析工具)我看到有一个char[]几乎使用了总内存的一半。 TIJmp 说char[] 来自SerializableCloneable。其中的值范围从字体列表和默认路径到消息和单个字符。

Serializable 在 JVM 中的确切行为是什么?因此,它是否始终保持“持久”副本,从而使我的内存占用量增加了一倍?如何在运行时编写对象的二进制副本而不将 JVM 变成内存占用者?

PS:内存消耗增加最多的方法如下。该文件大约有 229,000 行,每行 202 个字段。

public void readThetas(String filename) throws Exception
{
    long t1 = System.currentTimeMillis();
    documents = new HashMapX<String,double[]>(); //Document names to indices.
    Scanner s = new Scanner(new File(filename));
    int docIndex = 0;
    if (s.hasNextLine())
        System.out.println(s.nextLine()); // Consume useless first line :)
    while(s.hasNextLine())
    {
        String[] fields = s.nextLine().split("\\s+");
        String docName = fields[1];
        numTopics = fields.length/2-1;
        double[] thetas = new double[numTopics];
        for (int i=2;i<numTopics;i=i+2)
            thetas[Integer.valueOf(fields[i].trim())] = Double.valueOf(fields[i+1].trim());
        documents.put(docName,thetas);
        docIndex++;
        if (docIndex%10000==0)
            System.out.print("*"); //progress bar ;)
    }
    s.close();
    long t2 = System.currentTimeMillis();
    System.out.println("\nRead file in "+ (t2-t1) +" ms");
}

哦!而且 HashMapX 是这样声明的内部类:

public static class HashMapX< K, V> extends HashMap<K,V> {
    public V get(Object key, V altVal) {
        if (this.containsKey(key))
            return this.get(key);
        else
            return altVal;
    }
}

【问题讨论】:

  • 你能展示一些代码示例吗?
  • 请发布表明 Serializable 增加内存占用的测试。如果您可以发布显示您的 Map 正在使用的大量 RAM 的代码,那也会有所帮助。
  • 让我看看我是否明白你在上面的说法。你是说通过声明一个类 Serializable ,它的实例占用的大小比它是瞬态的要大?
  • 好的。所以,我无法重现我之前关于实现 Serializable 接口的结果,所以我将删除帖子的那部分。它可能是 JVM 与 tijmp 的混淆。然而,出于某种原因,tjimp 仍然报告来自 Serializable 和 Cloneable 的 char[] 不受控制地增长。即使在我完全摆脱了 HashMap 并使用了 double[][] 和 String[] 对象之后,这种情况也会发生。我的班级本身并没有实现 Serializable,但 tijmp 报告说有些东西正在使用并且正在使用大量内存。任何帮助表示赞赏。

标签: java memory heap-memory serializable cloneable


【解决方案1】:

这可能无法解决您的所有问题,但序列化可以显着增加内存使用量:http://java.sun.com/javase/technologies/core/basic/serializationFAQ.jsp#OutOfMemoryError

简而言之,如果您保持ObjectOutputStream 处于打开状态,那么除非您显式调用其reset() 方法,否则已写入其中的所有对象都不会被垃圾回收。

【讨论】:

  • 这是一个很好的线索,前提是所讨论的对象实际上正在被序列化,因为原始帖子仅建议通过使类可序列化来增加内存,并且开发人员对不可序列化的虚拟类和确定的内存占用更小(我们还不知道这个评估是如何进行的),但如果是这种情况,那么根本情况应该是别的东西。老实说,我太倾向于相信你的解释是迄今为止最合乎逻辑的解释。
【解决方案2】:

所以,我找到了答案。这是我的代码中的内存泄漏。与 Serializable 或 Cloneable 无关。

此代码正在尝试解析文件。每行包含一组我试图提取的值。然后,我保留其中一些值并将它们存储在 HashMapX 或其他结构中。

问题的核心在这里:

        String[] fields = s.nextLine().split("\\s+");
        String docName = fields[1];

我在这里传播它:

        documents.put(docName,thetas);

发生的情况是 docName 是对数组(字段)中元素的引用,我在程序的整个生命周期中都保留了该引用(通过将其存储在全局 HashMap 文档中)。只要我保持该引用有效,就不能对整个 String[] 字段进行垃圾收集。解决办法:

        String docName = new String(fields[1]); // A copy, not a reference.

从而复制对象并释放对数组元素的引用。这样,垃圾收集器可以在我处理完每个字段后释放数组使用的内存。

我希望这对所有使用 split 解析大型文本文件并将一些字段存储在全局变量中的人有用。

感谢大家的 cmets。他们引导我朝着正确的方向前进。

【讨论】:

    猜你喜欢
    • 2020-05-09
    • 2011-12-27
    • 2013-11-26
    • 1970-01-01
    • 1970-01-01
    • 2013-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多