Java 中的可序列化、可克隆和内存使用

我正在使用一个内部类,它是HashMap的子类。我有一个作为键和值。我存储大约200双打每个.我应该使用大约700 MB来存储密钥,指针和双精度。但是,内存分析显示,我需要的远不止于此(略高于2 GB)。Stringdouble[]double[]

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

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

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;
    }
}

答案 1

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

简而言之,如果保持一个打开状态,那么除非显式调用其方法,否则无法对已写入该对象的任何对象进行垃圾回收。ObjectOutputStreamreset()


答案 2

所以,我找到了答案。这是我的代码中的内存泄漏。与可序列化或可克隆无关。

此代码正在尝试分析文件。每行都包含一组我正在尝试提取的值。然后,我保留其中一些值,并将它们存储在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解析大型文本文件并将某些字段存储在全局变量中的人都有用。

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


推荐