Scala—对本地范围内的大量文件执行XML.loadFile时堆使用率高

Scala—对本地范围内的大量文件执行XML.loadFile时堆使用率高,scala,heap-memory,Scala,Heap Memory,我正在尝试从大量XML创建对象树。但是,当我在大约2000个xml文件(从100KB到200MB)上运行以下代码时(注意,我已经注释掉了创建对象树的代码),我得到了8-9GB的大内存占用。在下面的示例中,我希望内存占用最小,因为代码不包含任何引用,它只是创建元素并将其丢弃。运行完整GC后,堆内存保持不变 def addDir(dir: File) { dir.listFiles.filter(file => file.getName.endsWith("xml.gz")).foreach

我正在尝试从大量XML创建对象树。但是,当我在大约2000个xml文件(从100KB到200MB)上运行以下代码时(注意,我已经注释掉了创建对象树的代码),我得到了8-9GB的大内存占用。在下面的示例中,我希望内存占用最小,因为代码不包含任何引用,它只是创建元素并将其丢弃。运行完整GC后,堆内存保持不变

def addDir(dir: File) {
dir.listFiles.filter(file => file.getName.endsWith("xml.gz")).foreach { gzipFile =>
    addGzipFile(gzipFile)
}
}
def addGzipFile(gzipFile: File) {
val is = new BufferedInputStream(new GZIPInputStream(new FileInputStream(gzipFile)))
val xml = XML.load(is)
// parse xml and create object tree
is.close()
}
我的JVM选项是:-server-d64-Xmx16G-Xss16M-XX:+doescapealysis-XX:+UseCompressedOops

jmap-histo的输出如下所示

num #instances #bytes class name ---------------------------------------------- 1: 67501390 1620033360 scala.collection.immutable.$colon$colon 2: 37249187 1254400536 [C 3: 37287806 1193209792 java.lang.String 4: 37200976 595215616 scala.xml.Text 5: 18600485 595215520 scala.xml.Elem 6: 3420921 82102104 scala.Tuple2 7: 213938 58213240 [I 8: 1140334 36490688 scala.collection.mutable.ListBuffer 9: 2280468 36487488 scala.runtime.ObjectRef 10: 1140213 36486816 scala.collection.Iterator$$anon$24 11: 1140210 36486720 scala.xml.parsing.FactoryAdapter$$anonfun$startElement$1 12: 1140210 27365040 scala.collection.immutable.Range$$anon$2 ... Total 213412869 5693850736 num#实例#字节类名 ---------------------------------------------- 1:67501390 1620033360 scala.collection.immutable.$colon$colon 2:37249187 1254400536[C] 3:37287806 1193209792 java.lang.String 4:37200976 595215616 scala.xml.Text 5:18600485 595215520 scala.xml.Elem 6:342092182102104 scala.Tuple2 7:21393858213240[I] 8:1140334 36490688 scala.collection.mutable.ListBuffer 9:2280468 36487488 scala.runtime.ObjectRef 10:1140213 36486816 scala.collection.Iterator$$anon$24 11:1140210 36486720 scala.xml.parsing.FactoryAdapter$$anonfun$startElement$1 12:1140210 27365040 scala.collection.immutable.Range$$anon$2 ... 总计213412869 5693850736
我无法复制此行为。我使用以下程序:

import java.io._
import xml.XML

object XMLLoadHeap {

  val filename = "test.xml"

  def addFile() {
    val is = new BufferedInputStream(new FileInputStream(filename))
    val xml = XML.load(is)
    is.close()
    println(xml.label)
  }

  def createXMLFile() {
    val out = new FileWriter(filename)
    out.write("<foo>\n")
    (1 to 100000) foreach (i => out.write("  <bar baz=\"boom\"/>\n"))
    out.write("</foo>\n")
    out.close()
  }

  def main(args:Array[String]) {
    println("XMLLoadHeap")
    createXMLFile()
    (1 to args(0).toInt) foreach { i => 
      println("processing " + i)
      addFile()
    }
  }

}
导入java.io_
导入xml.xml
对象XMLLoadHeap{
val filename=“test.xml”
def addFile(){
val is=new BufferedInputStream(新文件输入流(文件名))
val xml=xml.load(is)
is.close()
println(xml.label)
}
def createXMLFile(){
val out=新文件编写器(文件名)
输出。写入(“\n”)
(1到100000)foreach(i=>out.write(“\n”))
输出。写入(“\n”)
结束
}
def main(参数:数组[字符串]){
println(“xmloadheap”)
createXMLFile()
(1到args(0).toInt)foreach{i=>
println(“处理”+i)
addFile()
}
}
}
我使用以下选项运行它:
-Xmx128m-XX:+HeapDumpOnOutOfMemoryError-verbose:gc
,它基本上看起来可以无限期运行

您可以尝试在仅使用最大的XML文件时查看它是否会这样做。问题可能不在于处理许多文件,而只是处理最大的文件。在64位计算机上使用一个伪200MB XML文件进行测试时,我发现我需要大约3G的内存。如果是这样,您可能需要使用拉式解析器。请参阅


除此之外,假设您不创建对象树,您可以使用
-Xmx4G-XX:+HeapDumpOnOutOfMemoryError
,然后使用.4GB之类的工具分析堆转储,这样就足以解析最大的XML文件,并且在出现内存不足错误时,可能已经分配了足够的对象来确定预处理的对象正在释放GC。很可能是一个对象保留了各种已解析的XML对象。

对单个最大的XML文件(438MB)运行程序(从scala控制台,使vm保持活动状态)。堆使用似乎不是问题运行程序(从scala控制台,使vm保持活动状态)对单个最大的XML文件(438MB)加载文件并运行完整gc后,获取了堆摘要。堆的使用似乎不是问题,因为只使用了111MB的旧一代(以及0个新一代)。但是,
top
命令的输出显示剩余(RES)大小为4.8GB。另一方面,以32位(3GB)运行堆抛出:java.lang.OutOfMemoryError:com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:501)上的scala.xml.parsing.FactoryAdapter.startElement(FactoryAdapter.scala:136)超过了GC开销限制在com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanStartElement(XMLDocumentFragmentScannerImpl.java:1363)在com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next…@Sachin这是在解析单个文件吗?我不会太担心顶部的RES stat。如果使用-Xmx16G,我希望JVM会在某种程度上利用它。在表示XML时,Scala似乎相当占用内存,但我不这么认为“我不认为它不必要地保留引用。根据您的尝试,使用XML.load可能不是正确的方法。@huynhjl我现在已经切换到XMLEvenReader。并且可以将21G XML加载到一个堆大小为16G的对象树中。有两件事很有帮助:1)使用字符串内部化和2)使用压缩doop。