Java StringBuilder数据块线程量大

Java StringBuilder high volumes of data blocks thread

我是 运行 一个处理数据库中大约 215K 条记录的小程序。这些记录包含 JaxB 用于编组和解组对象的 xml。

我的程序 运行 试图找到 xml 由于遗留问题而无法再解组的程序。每次遇到解组异常时,我都会将包含 xml 的异常消息保存在数组列表中。最后我想发送一封邮件,其中包含所有失败的记录以及原因异常消息。所以我使用 arraylist 中的消息和 StringBuilder 来组成电子邮件正文。

然而,大约有 75K 次失败,当我构建主体时,StringBuilder 只是在 for 循环中的某个点停止追加,线程被阻塞。我改变了我的方法,不再从异常消息中附加 xml,但我仍然不清楚为什么它不起作用。

可能是虚拟机内存不足,或者字符串只能是一定大小(怀疑我相信肯定是在 64 位时代)。有没有更好的方法可以解决这个问题?我考虑将 StringBuilder 发送到我的服务,而不是先将字符串保存在数组列表中,但那将是一个肮脏的接口:(

任何架构见解将不胜感激。

编辑 正如此处要求的代码,这不是火箭科学。假设失败列表包含大约 75K 个条目,每个条目包含一个 xml 平均 500 到 1000 行

  private String createBodyMessage(List<String> failures) {
    StringBuilder builder = new StringBuilder();
    builder.append("Failed operations\n");
    builder.append("=================\n\n");
    for (String failure : failures) {
      builder.append(failure);      
      builder.append("\n");      
    }
    return builder.toString();
  }

StringBuffer基于Array结构,数组最大单元格数为2^31-1
达到这个大小通常会在 Java 7 上引发错误,但我不太确定

解决方案是在达到 StringBuffer

的固定大小之前,将数据交换 到一个文件中

你可能会成功

int sizeEstimate = failures.size() * 20;
StringBuilder builder = new StringBuilder(sizeEstimate);
builder.append("Failed operations\n");
builder.append("=================\n\n");
while (!failures.isEmpty()) {
    builder.append(failures.remove(0));      
    builder.append('\n');      
}

这减少了调整 StringBuilder 内部缓冲区的大小,并消耗了减少内存的失败。

如果文本太大,可能无法解决问题。

但是压缩附件是标准程序。

Could it be that the VM went out of memory,

如果你填满了堆,你会得到一个 OutOfMemoryError 异常。

or can Strings only be of a certain size (doubtful I believe certainly in the 64 bit era).

事实上,是的。一个 Java StringStringBuilder 最多可以包含 2^32-1 个字符1.

Is there a better way I could have solved this ? I contemplated sending the StringBuilder to my service instead of saving the strings in an arraylist first ...

如果真正的问题是字符串的串联太大而无法容纳在 StringBuilder 中,那将无济于事。

实际上,更好的方法是将字符串流式传输到 PipedOutputStream,并使用相应的 PipedInputStream 构造一个 MimeBodyPart,然后将其附加到电子邮件。您也可以在流堆栈中包含一个压缩器。

但是 更好的方法 是不要尝试将数 GB 的错误数据作为电子邮件附件发送。将它们保存为可以获取的文件(或其他)如果电子邮件收件人需要它们。


1 - 令人惊讶的是,javadocs 似乎没有明确说明这一点。但是,String.length() returns 和 int 以及各种字符串操作方法采用 int 参数来指定偏移量和长度。当然,StringStringBuilder 的标准实现使用单个 char[] 作为后备存储,并且 JLS and 将数组限制为 2^31-1 个元素 JVM 规范。