当读取非常大的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?
由于
答案 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文件拆分为多个部分,然后您可以毫无问题地处理较小的部分。