【问题标题】:replace with big files java heap space out of memory用大文件替换java堆空间内存不足
【发布时间】:2021-12-15 13:07:10
【问题描述】:

我有一个 250mb 的大 xml 文档,其中一个标签包含我需要处理的另一个 xml。

但问题是,这个 xml 是由 CDATA 包装的,如果我尝试做一个 replace/replaceAll

String xml= fileContent.replace("<![CDATA[", "  ");
String replace = xml.replace("]]>", " ");

我开始了

java.lang.OutOfMemoryError: Java heap space

一个简单的结构示例。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<a>
    <b>
        <c>
            <![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="yes"?><bigXML>]]>
        </c>
    </b>
</a>

即使使用像 VDTSAX 这样的 XML 解析器也无济于事,因为我仍然需要删除 &lt;![CDATA[,而我们里面的内容是文件的最大部分。

分配更多内存堆不是一个选项,因为它在我没有任何 JVM 控制的机器上运行。

知道如何从 c 标签中提取 xml 并从 &lt;![CDATA[ 中提取

更新

正如我们在下面讨论的那样,我尝试使用 Streams 进行修改,但我仍然拥有outOfMemories

知道如何改进代码以避免错误吗?

private void readUpdateAndWrite(
    Reader reader,
    String absolutePath
) {
    // Write the content in file
    try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(absolutePath))) {
        // Read the content from file
        try (BufferedReader bufferedReader = new BufferedReader(reader)) {
            String line = bufferedReader.readLine();
            while (line != null) {
                String replace = line
                    .replace("<![CDATA[", " ")
                    .replace("]]>", " ");
                bufferedWriter.write(replace);
                line = bufferedReader.readLine();
            }
        } catch (IOException e) {
            logger.error("Error writing in file. Caused by {}", getStackTrace(e));
        }
    } catch (IOException e) {
        logger.error("Error reading in file. Caused by {}", getStackTrace(e));
    }
}

我发现了我的问题。 &lt;![CDATA[ 的内容是一个 256mb 的字符串行,所以我无法在该行中进行任何替换,否则我会得到 outOfMemory

如何将 256mb 的字符串分成新行。我试图通过大量字符串创建另一个InputStream,但没有工作。

我猜是因为是嵌入式 XML,我们不能有多行。

【问题讨论】:

  • 如何使用 SAX 或基于流的 API 解析大 XML 文件并提取您感兴趣的内容,而不是读取整个文件?
  • 是的,我用 VDT 做,但问题是文档的最大部分在 ]]>

标签: java string xml-parsing


【解决方案1】:

如果将整个文件作为内存中的字符串读取,则会出现内存不足。如果文件被逐块读取并执行您的操作,然后将该块与修改后的数据一起写入另一个文件,从而保存内存不足错误。

您可以尝试使用缓冲阅读器逐块读取:

BufferedReader buffer = new BufferedReader(file, int size);

【讨论】:

  • 我尝试使用 Stream,但我仍然遇到同样的问题。我猜是因为我在读取文件期间创建的字符串?否则我不明白
【解决方案2】:

您遇到的问题是您没有足够的内存来分配如此大字符串的副本。对String.replace 的调用将使用替换部分的副本创建一个新字符串。如果大多数文本都在这些标签内,而fileContent 是 250MB,那么您的双 replace 将在短时间内连续分配 2 x 250MB 字符串。

分配更多内存可以轻松解决此问题,但如果您说无法执行此操作,请尝试其他方式来加载字符串并扫描内容。一种方法是扫描文件标记位置并将匹配的部分保存到另一个文件。例如

String cdata = "<![CDATA[";
int start = fileContent.indexOf(cdata);
int end   = fileContent.lastIndexOf("]]>");

将剥离的部分写到另一个文件中。这不会在内存中实例化 250MB 字符串的第二个副本,并且应该为您留下包含 &lt;c&gt; 标记内的部分的文件以供持续处理。

try(var os = Files.newBufferedWriter(bigxml)) {
    os.write(fileContent, start+cdata.length(), end-start-cdata.length());
}

如果fileContent 中有多个开始/结束标记,这并不理想并且可能会失败。

【讨论】:

    猜你喜欢
    • 2021-11-20
    • 1970-01-01
    • 2020-01-23
    • 2014-01-04
    • 1970-01-01
    • 1970-01-01
    • 2011-10-08
    • 1970-01-01
    相关资源
    最近更新 更多