在java中处理大字符串时StringBuilder内存不足错误

Posted

技术标签:

【中文标题】在java中处理大字符串时StringBuilder内存不足错误【英文标题】:StringBuilder out of memory error when working with large strings in java 【发布时间】:2012-01-11 16:20:42 【问题描述】:

我从String test += str; 出发,test 以指数级增长,拥有成千上万个字符。运行了 45 分钟,可能是因为创建大字符串和删除垃圾。然后我像这样错开输入,使其达到 30 秒。

这似乎是一种便宜的方法,但效果很好:

  if (secondDump.length() > 50)
  
     intermedDump = intermedDump + secondDump;
     secondDump = "";
        

  if (intermedDump.length() > 100)
  
     thirdDump = thirdDump + intermedDump;
     intermedDump = "";
  
  if (thirdDump.length() > 500)
  
     fourthDump = fourthDump + thirdDump;
     thirdDump = "";
  
  if (fourthDump.length() > 1000)
  
     fifthDump = fifthDump + fourthDump;
     fourthDump = "";
  
  //with just this and not sixth.  Runtime>>>> : 77343
  if (fifthDump.length() > 5000)
  
     sixthDump = sixthDump + fifthDump;
     fifthDump = "";
  
  //with just this.  Runtime>>>> : 35903Runtime>>>> : 33780
  if (sixthDump.length() > 10000)
  
     fillerDump = fillerDump + sixthDump;
     sixthDump = "";
  

然后我发现StringBuilder 存在,从那以后我一直在尝试使用它,用它替换所有字符串操作。

问题是,我不断收到带有 java 内存堆溢出的 java.lang.OutOfMemoryError。我认为该字符串太长而无法作为StringBuilder 对象存储在内存中,因为它使我之前的代码在因内存不足错误而崩溃之前所做的进度大约是1/50。它只能处理可能不到一千个字符。

为什么一个字符串可以保存整个输出而这不能接近?另外,如果我将文本附加到JTextPane,那需要多少内存?如果我将StringBuilder 内容转储到JTextpane 并继续附加和清除StringBuilder,这似乎也不起作用。

这是现有的代码。页面只是一个被传递的对象:

protected void concatPlates(page PageA) throws IOException

   if (backend.isFirstPage == false)
   
      frontend.fillOutputPane("\n                                 " +
         "                                               \n", PageA);
      frontend.fillOutputPane("                                 " +
         "                                               \n", PageA);
      frontend.fillOutputPane("                                 " +
         "                                               \n", PageA);
   
   for (int i = 0; i < PLATELEN-1; i++)
   

      if (arrLeftCol[i].length() == 0)
      
         /////////////////////////////////////////////////////////
         /////////////////////////////////////////////////////////
         frontend.fillOutputPane(arrLeftCol[i].append(
           arrRightCol[i]));
      
     else
     
        PageA.tempStrBuff = new StringBuilder(arrLeftCol[i].substring(0,40));
        frontend.fillOutputPane(PageA.tempStrBuff.append(arrRightCol[i]));
     
     arrLeftCol[i].append("");
     arrRightCol[i].append("");

     backend.isFirstPage = false;
  



//this is the frontend class
public static void fillOutputPane(String s, page PageA)

   fillOutputPane(PageA.getStrBuf());

public static void fillOutputPane(StringBuilder stringBuild)

  try
  
     str.append(stringBuild);
  
  catch (java.lang.OutOfMemoryError e)
  
     System.out.println((str.length() * 16) /8);
     //System.out.println(str);
     System.out.println("out of memory error");
     System.exit(0);
  

这是错误:

Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Unknown Source)
at java.lang.AbstractStringBuilder.expandCapacity(Unknown Source)
at java.lang.AbstractStringBuilder.append(Unknown Source)
at java.lang.StringBuilder.append(Unknown Source)
at java.lang.StringBuilder.append(Unknown Source)
at backend.fill(backend.java:603)
at frontend$openL.actionPerformed(frontend.java:191)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.AbstractButton.doClick(Unknown Source)
at javax.swing.plaf.basic.BasicMenuItemUI.doClick(Unknown Source)
at javax.swing.plaf.basic.BasicMenuItemUI$Handler.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$000(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)

我认为这就是堆栈跟踪:

java.lang.Exception: Stack trace
at java.lang.Thread.dumpStack(Unknown Source)
at frontend.fillOutputPane(frontend.java:385)
at page.concatPlates(page.java:105)
at backend.setPlate(backend.java:77)
at backend.fill(backend.java:257)
at frontend$openL.actionPerformed(frontend.java:191)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.AbstractButton.doClick(Unknown Source)
at javax.swing.plaf.basic.BasicMenuItemUI.doClick(Unknown Source)
at javax.swing.plaf.basic.BasicMenuItemUI$Handler.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$000(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)81240560
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

【问题讨论】:

你的虚拟机参数是什么?尝试增加堆大小。 能否请您在使用 StringBuilder 类的地方添加代码。错误更有可能存在。 更多细节会有所帮助。我怀疑内存中的字符串会让你内存不足。你有堆栈跟踪吗? 我调用了 Thread.dumpStack();。就高级编码而言,我是个菜鸟,我假设这是调用它时打印的堆栈跟踪。 【参考方案1】:

其中一件可能发生的事情是,在 Java 中,java.lang.String 得到了特殊处理。字符串是不可变的,因此 JVM 将每个 String 对象放在一个池中。这个池的作用,除其他外,它被用作一种“缓存”,如果您创建多个具有实际“文本”的 String 实例值相同时,将从池中重用相同的实例。通过这种方式,创建内部具有相同文本的大量 String 对象实例实际上将恢复为在内存中只有很少的实际 String 实例。另一方面,如果你使用相同的文本来初始化多个 StringBuilder 实例,它们实际上将是单独的实例(包含相同的文本),从而占用更多的内存。

另一方面,如果您使用 StribgBuilder(例如 new StringBuilder("a").append("b");)连接字符串(例如 String c = "a"+"b";),实际上会创建更多的对象实例。

【讨论】:

我明白了。那么您认为这可能是导致内存不足错误的原因吗?我不认为一个字符数少于一千的 StringBuilder 对象会占用这么多内存。 我刚刚意识到它可能有超过一千个字符。 你能用 Eclipse 调试一下,看看字符串有多长吗? 或者只是添加一些 System.out.println's 来告诉你每个字符串在不同点的长度。不确定您的代码在做什么,因此无济于事。祝你好运! 呃……是的,我的估计是错误的。当程序崩溃时,StringBuilder 长度为 81240560 个字符。【参考方案2】:

显然您的应用程序没有足够的内存来完成操作。所以你需要为你的虚拟机指定内存标志。您可以尝试以下方法:

 java -Xms256m -Xmx512m YourApp

地点:

您的程序在启动时分配的最小 Xms 内存(在示例中为 256 MB) 如果您的程序需要更多内存,则由您的程序分配 Xmx 最大内存(在示例中为 512 MB)

【讨论】:

这是一种快速修复解决方案,并不能真正扩展或解释使用 String 与 StringBuilder 如何实际影响内存消耗。 这不是一个快速的解决方案 - 您想要保留在内存中的数据越多,您需要的内存就越多。 Althug StringBuilder 可能需要比单个 String 对象更多的内存,因为它会为新数据打开一些空闲槽。 我是新手,这是我的商业应用程序。如果你在 Eclipse 中,你会把上面的代码放在哪里?只是在 args 中的运行配置中? 是的,在参数选项卡中 -> VM 参数

以上是关于在java中处理大字符串时StringBuilder内存不足错误的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Java 中处理大数据?

Java难点 | StringBuilder类/StringBuffer类

java基础系列:常用API的用法及区别

字符串拼接的五种方式

java String 的+操作导致的问题

Java:InputStream 读取大文件太慢