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.