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

Я пытаюсь создать дерево объектов из большого количества 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: -сервер -d64 -Xmx16G -Xss16M -XX:+DoEscapeAnalysis -XX:+UseCompressedOops

И вывод jmap -histo выглядит так

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:       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
...
Итого 213412869 5693850736

1 ответ

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

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.

Другие вопросы по тегам