将BufferedReader转换为ByteArrayInputStream时如何提高内存使用率?

时间:2017-11-09 20:24:33

标签: java string memory

当读取非常大的XML字符串并将它们转换为Document对象时,我遇到了一些内存不足异常。

我这样做的方法是打开XML文件的URL流,将其包装在InputStreamReader中,然后将其包装在BufferedReader中。

然后我从BufferedReader读取并附加到StringBuffer:

StringBuffer doc = new StringBuffer();
BufferedReader in = new BufferedReader(newInputStreamReader(downloadURL.openStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
  doc.append(inputLine);
}

现在这是我遇到问题的部分。我在StringBuffer上使用toString,以便能够获取字节来创建一个字节数组,然后用于创建ByteArrayInputStream。我相信这一步导致我在内存中有两次相同的数据,是吗?

这是我正在做的事情:

byte xmlBytes[] = doc.toString().getBytes();
ByteArrayInputStream is = new ByteArrayInputStream(xmlBytes);
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
Builder xmlBuilder = new Builder(xmlReader,false);
Document d = xmlBuilder.build(is);

有没有办法可以避免创建重复内存(如果我这样做的话)或者有没有办法将BufferedReader直接转换为ByteArrayInputStream?

由于

2 个答案:

答案 0 :(得分:0)

以下是使用InputStream解析器消费Document来创建DOM的方法:

DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document document = builder.parse(inputStream);

这会创建更少的中间副本。但是,如果XML文档非常大,而不是在内存中完全解析它,最好的解决方案是使用StAX解析器。

使用StAX parser,您无法将整个已解析的文档加载到内存中。相反,您处理顺序找到的每个元素(并立即抛弃该元素)。

以下是一个很好的解释:Java: Parsing XML files: DOM, SAX or StAX?

还有SAX解析器,但使用StAX要容易得多。在此讨论:When should I choose SAX over StAX?

答案 1 :(得分:0)

如果您的XML(或JSON)文件很大,那么将整个内容加载到内存不是一个好主意,因为正如您所提到的,解析过程会消耗大量内存。

如果有更多用户,这个问题会更严重(我的意思是多一个线程)。试想一下,如果您的应用程序需要提供两个,十个或更多并行请求,将会发生什么......

将大文件作为流处理的最佳方法,在您从流中读取有效负载后,您可以关闭它而不读取流直到结束。它更快,更友好的解决方案。

Apache Commons IO可以帮助您完成这项工作:

Activity

处理此问题的另一种方法是将XML文件拆分为多个部分,然后您可以毫无问题地处理较小的部分。