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

Java中的可序列化、可克隆和内存使用,java,memory,heap,serializable,cloneable,Java,Memory,Heap,Serializable,Cloneable,我使用的是一个内部类,它是HashMap的一个子类。我有一个字符串作为键,双[]作为值。我每个double[]存储大约200个double。我应该用大约700MB来存储键、指针和双精度键。然而,内存分析显示我需要的远不止这些(2GB多一点) 使用(分析工具)我看到有一个char[]占用了几乎一半的内存。TIJmp说char[]来自Serializable和Cloneable。其中的值范围从字体列表和默认路径到消息和单个字符 在JVM中,Serializable的确切行为是什么?它是否始终保持一个

我使用的是一个内部类,它是HashMap的一个子类。我有一个
字符串
作为键,
双[]
作为值。我每个
double[]
存储大约200个double。我应该用大约700MB来存储键、指针和双精度键。然而,内存分析显示我需要的远不止这些(2GB多一点)

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

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

PS:内存消耗增加最多的方法是下面的方法。该文件大约有229000行,每行有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");
}
public void readThetas(字符串文件名)引发异常
{
long t1=System.currentTimeMillis();
documents=new HashMapX();//索引的文档名。
扫描仪s=新扫描仪(新文件(文件名));
int-docIndex=0;
如果(s.hasNextLine())
System.out.println(s.nextLine());//第一行:)
而(s.hasNextLine())
{
字符串[]字段=s.nextLine().split(\\s+);
字符串docName=字段[1];
numTopics=fields.length/2-1;
double[]θ=新的double[numTopics];
对于(int i=2;i扩展HashMap){
公共V get(对象键,V altVal){
如果(本文件包含密钥))
返回此。获取(键);
其他的
返回altVal;
}
}

这可能无法解决您的所有问题,但序列化可以显著提高内存使用率:


简而言之,如果您保持一个
ObjectOutputStream
处于打开状态,则除非显式调用其
reset()
方法,否则无法对写入该流的对象进行垃圾收集。

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

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

问题的核心在于:

        String[] fields = s.nextLine().split("\\s+");
        String docName = fields[1];
我在这里传播:

        documents.put(docName,thetas);
发生的情况是docName是对数组(字段)中某个元素的引用,我将在程序的整个生命周期内保留该引用(通过将其存储在全局HashMap文档中)。只要我保持该引用处于活动状态,就不能对整个字符串[]字段进行垃圾收集。解决方案:

        String docName = new String(fields[1]); // A copy, not a reference.
这样,当我处理每个字段时,垃圾收集器就可以释放数组使用的内存

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


谢谢大家的评论。他们给了我正确的方向。

你能展示一些代码示例吗?请发布显示可序列化可增加内存占用的测试。如果你能发布显示地图使用了大量RAM的代码,那也会有所帮助。让我看看我是否理解你在上面的陈述。你是说by声明一个类是可序列化的,它的实例占用的大小比它是瞬态的要大?好的。所以,我无法重现我以前关于实现可序列化接口的结果,所以我将删掉这篇文章的这一部分。它可能是JVM与tijmp混在一起的。但是,出于某种原因,tjimp仍然报告char[]来自Serializable和Cloneable,它们在不受控制的情况下增长。即使在我完全摆脱HashMap并使用double[]]和String[]之后,这种情况也会发生对象。我的类本身并没有实现可序列化,但tijmp报告说有些东西是可序列化的,并且正在使用大量内存。非常感谢任何帮助。这是一个很好的线索,前提是所讨论的对象实际上正在序列化,因为最初的帖子只建议通过使类可序列化和开发人员增加内存在不可序列化的伪类上执行测试并确定内存占用更小(我们还不知道如何执行此评估),但如果是这种情况,则根情况应该是其他情况。老实说,我太倾向于相信您的解释是迄今为止最符合逻辑的解释。
        String docName = new String(fields[1]); // A copy, not a reference.