2017-11-09 3 views
0

非常に大きなXML文字列を読み込んでDocumentオブジェクトに変換するときに、メモリ不足の例外が発生しています。BufferedReaderをByteArrayInputStreamに変換する際のメモリ使用量を改善する方法は?

私がこれをやっているのは、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); 
} 

今これは私が問題を抱えてい一環です。私はByteArrayInputStreamを作成するために使用されるバイト配列を作成するためにバイトを取得できるように、StringBufferのtoStringを使用しています。私はこのステップが私にメモリ内で同じデータを2回持たせていると信じています。ここで

は私がやっているものです:

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?

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

これはあまり仲介コピーを作成します。ここでは

おかげ

+0

"重複メモリ"は作成していません。あなたはあなたの記憶のすべてを持っています。このStringBufferはどこに行きたいのですか? –

+0

私が言ったのは、同じデータをメモリに2回作りましたか?最終的には、Documentオブジェクトにデータが必要です。 – Seephor

答えて

0

あなたがDOMパーサーを使用してDocumentを作成するInputStreamを消費することができる方法です。ただし、XML文書が非常に大きい場合は、メモリ内で完全に解析するのではなく、StAXパーサを使用するのが最善の方法です。

StAX parserを使用すると、解析されたドキュメント全体がメモリにロードされません。代わりに、順番に見つかった各要素を処理します(要素はすぐに破棄されます)。ここで

は良い説明です:Java: Parsing XML files: DOM, SAX or StAX?

SAXのパーサーもありますが、それはStAXを使用する方がはるかに簡単です。ここでの議論:When should I choose SAX over StAX?

+0

しかし、StringBufferでtoStringを呼び出さずに正しいByteArrayInputStreamを作成するにはどうすればよいですか?あなたが提案したものは、私が第2セクションですでに持っていたものと同じように見えます。または、StringBufferを使用する代わりに、BufferedReaderでラップされた元のInputStreamReaderを使用できると言っていますか?それは不明だ。 – Seephor

0

あなたのXML(またはJSON)ファイルが大きい場合は、言及したように膨大なメモリを消費するため、内容全体をメモリにロードすることは好ましくありません。

この問題は、より多くのユーザーがいる場合(これは1つ以上のスレッドを意味します)、より深刻になります。アプリケーションが2つ、10つ、またはそれ以上の並列要求を処理する必要がある場合にどうなるか想像してみてください。

巨大なファイルをストリームとして処理する最も良い方法は、ストリームからペイロードを読み取った後、終わりまでの流れ。より高速でメモリに優しいソリューションです。

Apache Commons IOは仕事をするためにあなたを助けることができます。

LineIterator it = FileUtils.lineIterator(theFile, "UTF-8"); 
try { 
    while (it.hasNext()) { 
     String line = it.nextLine(); 
     // do something with line 
    } 
} finally { 
    LineIterator.closeQuietly(it); 
} 

この問題を処理するための別の方法は、部品にあなたのXMLファイルを分割し、その後、あなたはどんな問題なく小さな部分を処理することができます。

+0

これは、すでにパース部分の行単位で読み込まれています。バイト配列出力ストリームに変換して、バッファと同じ内容の新しい文字列を作成するときだけです。バイトをXMLに変換する際に行単位で指定しない限り。その場合、XMLReaderを使用してこのアプローチをどのように実行しますか – Seephor

関連する問題