网络爬虫内存溢出问题
问题:
SparkStreaming 流式流式任务总是异常退出,看过worker的日志后发现再爬取一个二进制文件时会出现堆内存溢出的问题,将该文件下载下来后发现该文件的大小只有8m左右,我们的任务设置的worker内存为3G,正常来说是不会导致内存溢出的。网络爬虫的框架使用的是webmagic,于是单独对这一段代码进行了debug看看。
报错堆栈信息:
Exception in thread "pool-1-thread-1" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOfRange(Arrays.java:3664) at java.lang.String.<init>(String.java:201) at java.lang.String.substring(String.java:1956) at java.lang.String.trim(String.java:2865) at org.jsoup.nodes.Element.text(Element.java:864) at us.codecraft.xsoup.xevaluator.ElementOperator$AllText.operate(ElementOperator.java:44) at us.codecraft.xsoup.xevaluator.DefaultXElement.get(DefaultXElement.java:31) at us.codecraft.xsoup.xevaluator.DefaultXElement.get(DefaultXElement.java:24) at us.codecraft.xsoup.xevaluator.DefaultXElements.list(DefaultXElements.java:47) at us.codecraft.webmagic.selector.XpathSelector.selectList(XpathSelector.java:31) at us.codecraft.webmagic.selector.HtmlNode.selectElements(HtmlNode.java:80) at us.codecraft.webmagic.selector.HtmlNode.xpath(HtmlNode.java:43)
根据提示得知时xpath部分的代码导致的,我们这行代码如下:
page.getHtml.xpath("///allText()")
查了下xpath的语法,发现没有///这种语法形式,
/ 从根节点选取。
表达式 | 描述 |
---|---|
/ | 从根节点选取。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点 |
.. | 选取当前节点的父节点。 |
这部分代码不止怎么写成了///,但是并不会报入参错误。让我们来看看源码是怎么写的:
一路调试发现此处有一个编译xpath字符串的代码:
public XpathSelector(String xpathStr) { this.xPathEvaluator = Xsoup.compile(xpathStr); }
这部分字符串会根据【// / |】这三个字符做一个拆分:
那么所匹配的路径相当于 ""和allText()
xpath被编译成""的部分除了问题,在查询element的时候会生成大量的对象,导致内存的溢出。为什么会查询到大量的元素呢?
当匹配规则为空的时候,会将每一个标签页当成一个元素。例如下图中的tag="w=w"
还有一个问题,为什么一个二进制文件会有那么多的标签呢?
当this.html ==null的时候,会调用new Html创建,在创建后会对原来的数据添加很多标签
public Html getHtml() { if (this.html == null) { this.html = new Html(this.rawText, this.request.getUrl()); } return this.html; }
T�T�T�*U+U,U-U.U/U0U1U2U3U4U5U6U7U8U9U:U;U <w=w>
那么在调用selectelement的时候,会将每一个标签映射为一个对象,导致创建了2000多个对象。