JAVA が XML を解析するときのメモリ消費の問題

問題がありました

最近のプロジェクトでは、外部から渡されたXMLからデータを読み込み、データベースに書き込む機能が必要です。
書き込み後、Tomcat をローカル コンピューターで実行し、XML 内のデータを通常どおり読み取り、整理した後、データベースに挿入して保存しました。ただし、オンラインで実行すると、この機能が原因で Tomcat が直接クラッシュするという問題がよく発生します。

最初はオンライン環境かと思いきや、外部からインポートしたXMLファイルが大量にあり、XMLファイルの一覧を読み込む際に、再帰的な方法ですべてのXMLファイルを検索していたため、問題が発生していました。多層ディレクトリ. 後で、ディレクトリ階層が深くないことが判明しました, 再帰は問題にならないはずです.

その後、何百もの XML ファイル (それぞれ約 7MB) がテスト用にローカル コンピューター上に作成されましたが、これらのファイルはデータベースに対して正常に読み書きできました。あるラウンドのテスト中に、ローカル コンピューターのメモリ使用量を誤って調べたところ、実行プロセス中にメモリ使用量が 7.8G から 9.4G に上昇し、実行が終了するまで安定していたことがわかりました。 7.8Gに戻りました。

現時点で明らかな問題は、XML ファイルの読み取りプロセスが大量のメモリを消費し、オンライン Tomcat の合計ヒープ メモリが 1GB しかないため、これをサポートできないことです。

これは私の好奇心をかき立てました.ここで、XMLはわずか7MBですが、それを読み取るときにどれくらいのメモリを消費できますか?

そこで、JV (jdk/bin ディレクトリにある jvisualvm.exe) を開きました。プログラムがXMLファイルを読み込むところにブレークポイントを置き、監視結果は以下の通りです
XMLファイルを読む前に
XMLファイルを読み込んだ後
。ローカル コンピューター上の Tomcat の総ヒープ メモリも私が 1GB に設定します。Tomcat の既定のルールによると、Eden スペースはサイズの 1/3 です)、XML ファイルを直接読み取ると 150M を使用し、占有された Eden メモリは190M に達し、別の 1 つがすぐにいっぱいになったので、GC のリリースが少し遅くなる限り、直接実行されます もう動きません (私のローカル コンピューターの GC は高速なので、まだ正常に実行できます。つまり、より多くの GC 時間があります)

その後、情報を確認したところ、私が使用したdom読み取り方法は比較的メモリを大量に消費することがわかりましたが、この方法はXMLコンテンツを変更できます(ただし、この要件にはこの関数は必要ありません)、他のSAXは消費量が少なくなりますメモリー。
ここに画像の説明を挿入

そこでXMLの読み込み方法を変更し、ようやくオンラインのTomcat(ヒープメモリ1GB)で大量のXMLファイルを正常に読み込めるようになりました。1000 個の XML 監視結果をローカルで読み取るには、次の 2 つの方法があり、GC の回数が 2 倍になります。
XML、GC 時間を読み取る Dom の方法
XML を sax モードで読み取り、GC 時間

以下で説明します. 修正された読み取り方法は、JAVA に付属の JAXBContext (javax.xml.bind.JAXBContext) を使用します.
1. まず、XML と同じ構造を持つオブジェクトが必要です. 多層ノードの場合、オブジェクト参照のみが必要です。

@XmlRootElement(name = "ROOT") //注解法指定根元素/对象与元素绑定,大小写需要一致;也可以换设置法
public static class Root{
    
    
	BookVo bookVo; //java对象中的属性名可以和节点名不一致
	public BookVo getBookEle() {
    
     //对象必做有get/set,且需要和XML节点名对应,<ROOT><bookEle>...</bookEle></ROOT>
		return viosurveilVo;
	}
	public void setBookEle(BookVo bookVo) {
    
    
		this.viosurveilVo = viosurveilVo;
	}
}
public static BookVo{
    
    ...}
//解析指定xml文件
private Root analysisXML(String xmlFilePath) {
    
    
	try {
    
    
		JAXBContext jaxbContext = JAXBContext.newInstance(Root.class);
		Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
		StreamSource source = new StreamSource(new File(xmlFilePath));
		JAXBElement<Root> jaxbElement = unmarshaller.unmarshal(source, Root.class); //指定法,指定根元素/对象与元素绑定
		Root root = jaxbElement.getValue(); //直接得到可操作JAVA对象
		//Root root = (Root) unmarshaller.unmarshal(new File(xmlFilePath)); //使用@注解法,只需要这句,不需要上面3句
		//ViosurveilVo viosurveilVo = root.getViosurveil();
		return root;
	}catch (Exception e) {
    
    
		return null;
	}
}

おすすめ

転載: blog.csdn.net/babdpfi/article/details/130038466