Scala - высокое использование кучи при выполнении XML.loadFile для большого количества файлов в локальной области - PullRequest
5 голосов
/ 02 октября 2010

Я пытаюсь создать дерево объектов из большого количества xmls. Однако, когда я запускаю следующий код на примерно 2000 xml-файлах (в диапазоне от 100 КБ до 200 МБ) (обратите внимание, что я закомментировал код, который создает дерево объектов), я получаю большой объем памяти 8-9 ГБ. Я ожидаю, что объем памяти будет минимальным в следующем примере, потому что код не содержит ссылок, он просто создает Elem и выбрасывает его. Память кучи остается неизменной после запуска полной 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: + DoEscapeAnalysis -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

1 Ответ

2 голосов
/ 02 октября 2010

Я не могу воспроизвести это поведение. Я использую следующую программу:

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()
    }
  }

}

Я запускаю его со следующими параметрами: -Xmx128m -XX:+HeapDumpOnOutOfMemoryError -verbose:gc, и похоже, что он может работать бесконечно.

Вы можете попытаться увидеть, делает ли это это при использовании только самого большого файла XML. Возможно, проблема не в обработке большого количества файлов, а в обработке самого большого файла. При тестировании здесь с фиктивным 200-мегабайтным XML-файлом на 64-битной машине я вижу, что мне нужно около 3G памяти. Если это так, возможно, вам придется использовать парсер. См. XMLEventReader .

Кроме этого, при условии, что вы не создаете дерево объектов, вы можете использовать -Xmx4G -XX:+HeapDumpOnOutOfMemoryError и затем анализировать дамп кучи с помощью инструмента, подобного MAT . 4 ГБ должно быть достаточно для анализа самого большого файла XML, и к тому времени, когда вы получаете ошибку нехватки памяти, может быть достаточно объектов, выделенных, чтобы точно определить, какой объект препятствует GC. Скорее всего, это будет объект, удерживающий различные проанализированные объекты XML.

...