1

我有这段代码可以将 StringBuilders 的 ArrayList 转换为 Strings 的 ArrayList:

代码:

public ArrayList<String> convGenSeqToString(ArrayList<StringBuilder> buff){
        ArrayList<String> convBuf = new ArrayList<String>();
        
        for (StringBuilder xVar: buff){
            convBuf.add(xVar.toString());
        }
        return convBuf;
    }

我的代码适用于 15-20MB 文本文件的文件。但是,我有一个 44MB 的文本文件,每当我使用该文本文件运行程序时,我总是会收到此错误。

错误:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2746)
    at java.util.ArrayList.ensureCapacity(ArrayList.java:187)
    at java.util.ArrayList.add(ArrayList.java:378)
    at Recognition.convGenSeqToString(Recognition.java:157)
    at Recognition.genSeq(Recognition.java:145)
    at Recognition.Recognitions(Recognition.java:96)
    at ChainDetection.main(Detection.java:25)

我已经使用 -Xmx2048M 增加了 JVM 运行配置中的内存,但我仍然有同样的错误。我将错误定位在上面显示的代码中,并突出显示了这一行:

convBuf.add(xVar.toString());

有没有其他方法可以在不使用该.toString()方法的情况下将 StringBuilder 转换为字符串?我在其他论坛和这里也看到他们为 toString 创建了自定义类,但还不熟悉泛型和一些“@”关键字。有人对如何解决这个问题有建议或指导吗?

已编辑

我按照vanza的建议编辑了我的代码:

public ArrayList<String> convGenSeqToString(ArrayList<StringBuilder> buff){
        ArrayList<String> convBuf = new ArrayList<String>(buff.size());
        Iterator <StringBuilder> iterBuf = buff.iterator();
        
        while (iterBuf.hasNext()){
            StringBuilder x = iterBuf.next();
            convBuf.add(x.toString());
            iterBuf.remove();
        }
        return convBuf;
    }

错误信息:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.ArrayList.<init>(ArrayList.java:132)
    at Recognition.convGenSeqToString(Recognition.java:154)
    at Recognition.genSeq(Recognition.java:145)
    at Recognition.Recognitions(Recognition.java:96)
    at ChainDetection.main(ChainDetection.java:25)

我用 buff.size() 和没有 buff.size() 试过这个,我得到了同样的错误信息。但似乎我的错误信息现在减少到 5。对此还有其他想法吗?提前致谢!

4

2 回答 2

5

在不了解您的程序的更多信息的情况下,我不会对您采用的方法发表任何评论,并且仅对您发布的代码发表评论。不过,一个 44MB 的文件正在填充 2G 堆,这对我来说听起来很奇怪。

您可以做的一件事是预先在目标数组中分配空间:

ArrayList<String> convBuf = new ArrayList<String>(buf.size());

这将避免 ArrayList 调整大小步骤,该步骤创建现有列表的副本(并显示在您的堆栈跟踪中)。

您可以尝试的另一件事是在构建 String 数组时释放对原始 StringBuilders 的引用。使用迭代器(而不是“for each”循环),并在每次迭代时从buff数组中删除 StringBuilder;Iterator.remove()这样,如果内存不足,您将释放一些内存供垃圾收集器回收。

但是再一次,你用这么小的文件耗尽内存听起来很奇怪。也许用 jvisualvm 查看你的堆可以提供一些启示。

于 2012-07-08T19:26:42.407 回答
1

有两种方法可以从 StringBuilder 中获取价值

  1. 第一次使用Reflection并调用package protected方法 getValue() 这很难,我认为这不是很好。
  2. 第二次调用方法getChars

顺便说一句,您将拥有字符数组,并且为了使用string您将创建新的字符串实例,它将使用您的内存。

于 2012-07-08T19:32:59.407 回答