替换为大文件 java 堆 space 内存不足
replace with big files java heap space out of memory
我有一个很大的 xml 文档 250mb,其中一个标签包含我需要处理的另一个 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>
即使使用像 VDT
或 SAX
这样的 XML 解析器也无济于事,因为我仍然需要删除 <![CDATA[
并且我们里面的东西是最大的文件的一部分。
分配更多内存堆不是一个选项,因为 运行 在我没有任何 JVM 控制的机器上。
想知道如何从 c
标签中提取 xml 以及如何从 <![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));
}
}
我发现了我的问题。 <![CDATA[
的内容是一个 256mb 的字符串行,所以我无法在该行中进行任何替换,或者我得到 outOfMemory
.
如何将 256mb 的字符串分成新行。我试图通过大量字符串创建另一个 InputStream
,但没有用。
我猜是因为它是嵌入式 XML 并且我们不能有多行。
如果将整个文件作为字符串读取到内存中,就会出现内存不足的情况。如果逐块读取文件并执行操作,然后将修改后的数据写入另一个文件,从而避免内存不足错误。
您可以尝试使用缓冲 reader 逐块读取:
BufferedReader buffer = new BufferedReader(file, int size);
您遇到的问题是您没有足够的内存来分配如此大的字符串的副本。对 String.replace
的调用将创建一个带有替换部分副本的新字符串。如果大多数文本都在这些标签内并且 fileContent
是 250MB,那么您的双 replace
将在短时间内连续分配 2 x 250MB 的字符串。
分配更多内存可以轻松解决此问题,但如果您说您不能这样做,请尝试使用不同的方式来加载字符串并扫描内容。一种方法是扫描文件标记位置并将匹配的部分保存到另一个文件。例如
String cdata = "<![CDATA[";
int start = fileContent.indexOf(cdata);
int end = fileContent.lastIndexOf("]]>");
将剥离的部分写到另一个文件中。这不会在内存中实例化 250MB 字符串的第二个副本,并且应该为您留下包含 <c>
标记内的部分的文件以进行持续处理。
try(var os = Files.newBufferedWriter(bigxml)) {
os.write(fileContent, start+cdata.length(), end-start-cdata.length());
}
这并不理想,如果 fileContent
中有多个 start/end 标记,可能会失败。
我有一个很大的 xml 文档 250mb,其中一个标签包含我需要处理的另一个 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>
即使使用像 VDT
或 SAX
这样的 XML 解析器也无济于事,因为我仍然需要删除 <![CDATA[
并且我们里面的东西是最大的文件的一部分。
分配更多内存堆不是一个选项,因为 运行 在我没有任何 JVM 控制的机器上。
想知道如何从 c
标签中提取 xml 以及如何从 <![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));
}
}
我发现了我的问题。 <![CDATA[
的内容是一个 256mb 的字符串行,所以我无法在该行中进行任何替换,或者我得到 outOfMemory
.
如何将 256mb 的字符串分成新行。我试图通过大量字符串创建另一个 InputStream
,但没有用。
我猜是因为它是嵌入式 XML 并且我们不能有多行。
如果将整个文件作为字符串读取到内存中,就会出现内存不足的情况。如果逐块读取文件并执行操作,然后将修改后的数据写入另一个文件,从而避免内存不足错误。
您可以尝试使用缓冲 reader 逐块读取:
BufferedReader buffer = new BufferedReader(file, int size);
您遇到的问题是您没有足够的内存来分配如此大的字符串的副本。对 String.replace
的调用将创建一个带有替换部分副本的新字符串。如果大多数文本都在这些标签内并且 fileContent
是 250MB,那么您的双 replace
将在短时间内连续分配 2 x 250MB 的字符串。
分配更多内存可以轻松解决此问题,但如果您说您不能这样做,请尝试使用不同的方式来加载字符串并扫描内容。一种方法是扫描文件标记位置并将匹配的部分保存到另一个文件。例如
String cdata = "<![CDATA[";
int start = fileContent.indexOf(cdata);
int end = fileContent.lastIndexOf("]]>");
将剥离的部分写到另一个文件中。这不会在内存中实例化 250MB 字符串的第二个副本,并且应该为您留下包含 <c>
标记内的部分的文件以进行持续处理。
try(var os = Files.newBufferedWriter(bigxml)) {
os.write(fileContent, start+cdata.length(), end-start-cdata.length());
}
这并不理想,如果 fileContent
中有多个 start/end 标记,可能会失败。