我一直在尝试编写一个XML解析器来读取维基百科XML转储(英语,仅限当前版本,大约6.2Gb bzip)并且一直在使用Scala 2.8.1 pull解析器。它得到了合理的通过(超过1000万篇文章中的300万篇),但似乎逐渐泄漏内存并最终因出现堆错误而爆炸。我把堆高达1.5Gb并且它进一步(几乎到最后),但后来我得到(我忘了确切的异常)一个错误,表明垃圾收集器放弃了(花费了整个处理资源的很大一部分)没有回收太多)。
我的代码对我来说似乎很合理(虽然它还不是惯用的功能scala)但我看不到任何明显的泄漏源。我也知道拉解析器仍在改进 - 但我很清楚我自己的无知称这是一个库问题。我是一位经验丰富的C ++和Python程序员,但我刚刚进入Scala,所以我将不胜感激。
import java.io.{FileInputStream, BufferedInputStream}
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream
import org.apache.hadoop.io.SequenceFile.{createWriter}
import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.fs.{FileSystem, Path}
import org.apache.hadoop.io.Text
import org.apache.hadoop.io.SequenceFile.CompressionType.BLOCK
import scala.io.Source
import scala.xml.pull.{XMLEventReader, EvElemStart, EvElemEnd, EvText}
object Crunch
{
private def parsePage( parser : XMLEventReader ) : (String, Long, Long, String) =
{
var title = ""
var id = 0
var revision = 0
var text = ""
var done = false
while ( parser.hasNext && !done )
{
parser.next match
{
case EvElemStart(_, "title", _, _ ) =>
{
title = getText( parser, "title" )
}
/*case EvElemStart(_, "revision", _, _) =>
{
// Need to get the 'id' from revision
revision = getText( parser, "revision" ).toInt
}*/
case EvElemStart(_, "id", _, _ ) =>
{
id = getText( parser, "id" ).toInt
}
case EvElemStart(_, "text", _, _ ) =>
{
text = getText( parser, "text" )
}
case EvElemEnd(_, "page") =>
{
done = true
}
case _ =>
}
}
return (title, id, revision, text)
}
private def getText( parser : XMLEventReader, inTag : String ) : String =
{
var fullText = new StringBuffer()
var done = false
while ( parser.hasNext && !done )
{
parser.next match
{
case EvElemEnd(_, tagName ) =>
{
assert( tagName.equalsIgnoreCase(inTag) )
done = true
}
case EvText( text ) =>
{
fullText.append( text )
}
case _ =>
}
}
return fullText.toString()
}
def main( args : Array[String] )
{
require( args.length == 2 )
val fin = new FileInputStream( args(0) )
val in = new BufferedInputStream(fin)
val decompressor = new BZip2CompressorInputStream(in)
val runtime = Runtime.getRuntime
val conf = new Configuration()
val fs = FileSystem.get(conf)
//val writer = createWriter( fs, conf, new Path(args(1)), new Text().getClass(), new Text().getClass(), BLOCK )
var count = 0
try
{
val source = Source.fromInputStream( decompressor )
val parser = new XMLEventReader(source)
while (parser.hasNext)
{
parser.next match
{
case EvElemStart(_, "page", attrs, _) =>
{
val (title, id, revision, text) = parsePage( parser )
//writer.append( new Text(title), new Text(text) )
count = count + 1
if ( count % 100 == 0 )
{
printf("%s %d (%dMb mem, %dMb free)\n", title, count,
(runtime.totalMemory/1024/1024).toInt,
(runtime.freeMemory/1024/1024).toInt )
}
}
case _ =>
}
// Do something
}
}
finally
{
decompressor.close()
fin.close()
}
println( "Finished decompression.")
}
}
答案 0 :(得分:3)
XML pull解析器有两种类型的内存问题已在trunk中修复:
第一个问题通常会导致内存问题非常快,所以不太可能。
两者都应该每晚固定2.9.0,我建议使用它。如果你在2.9.0问题上运行,因为它是主干并且可能不稳定,你也可以通过下载和编译本地XMLEventEventReader
和MarkupParser
来反向移植这两个补丁,然后将输出打包为{ {1}}以便在scala libs jar之前将其放在2.8.1安装的00patch.jar
下。