HTML::TreeBuilder::XPath + WWW::Mechanize なスクリプトを永続化させようとしてはまった
WWW::Mechanize
でコンテンツとってきて TreeBuilder::XPath
で解析するようなのを今まで単機能スクリプトなどでは便利に使っていたのだけど、それを永続的なプロセスで使おうとしたらメモリ使いまくってとんでもないことになったという話。
まずどんどんメモリが増えていくのでタイミングよく読んでいたjrockwayの記事などを参考に、
perl -MDevel::Leak::Object=GLOBAL_bless
付きでスクリプトを実行してみる。
少しソースをいじってまずはループを一回で止めるようにしたときの出力
Tracked objects by class:
Config 1
DBI 1
DBI::var 5
DBIx::Class::ResultSource::Table 1
DateTime::Duration 2
DateTime::Format::Builder::Parser 3
DateTime::Format::Builder::Parser::Regex 10
DateTime::Infinite::Future 1
DateTime::Infinite::Past 1
DateTime::Locale::en 1
DateTime::TimeZone::Asia::Tokyo 1
DateTime::TimeZone::Floating 2
Encode::Internal 1
Encode::utf8 2
Errno 1
FileHandle 3
HTML::Element 1799
HTML::Element::_travsignal 5
HTML::TreeBuilder::XPath 1
XML::XPathEngine 1
XML::XPathEngine::Expr 19
XML::XPathEngine::Function 6
XML::XPathEngine::Literal 9
XML::XPathEngine::LocationPath 4
XML::XPathEngine::Root 1
XML::XPathEngine::Step 9
utf8 2
つぎ、少しループしてから止めたときの出力
Tracked objects by class:
Config 1
DBI 1
DBI::var 5
DBIx::Class::ResultSource::Table 1
DateTime::Duration 2
DateTime::Format::Builder::Parser 3
DateTime::Format::Builder::Parser::Regex 10
DateTime::Infinite::Future 1
DateTime::Infinite::Past 1
DateTime::Locale::en 1
DateTime::TimeZone::Asia::Tokyo 1
DateTime::TimeZone::Floating 2
Encode::Internal 1
Encode::utf8 2
Errno 1
FileHandle 3
HTML::Element 7378
HTML::Element::_travsignal 5
HTML::TreeBuilder::XPath 4
XML::XPathEngine 1
XML::XPathEngine::Expr 19
XML::XPathEngine::Function 6
XML::XPathEngine::Literal 9
XML::XPathEngine::LocationPath 4
XML::XPathEngine::Root 1
XML::XPathEngine::Step 9
utf8 2
となって、TreeBuilderがなんかやばいことになってるくさい。
で、マニュアル読んでたら $tree->delete
やれよ!って書いてあった。
TreeBuilderは明示的にdeleteメソッド呼ばないと綺麗になくならないらしい。たぶん常識なのだが、いままでやってなかったわぁ。。
これやったらずいぶんメモリ増えなくなった、でもまだなんか少しずつ増える。でも、Devel::Leak::Objectの出力は処理数あげてもかわらない。
あとはmechがあやしいなぁとおもって毎回newするようにしたらメモリ増えなくなったのであれれとおもってマニュアル見たら、デフォルトではヒストリを無限に記録していくらしい。
なので
WWW::Mechanize->new( stack_depth => 1 )
とかしたら大丈夫になった。これでとりあえず大丈夫かな。
まぁ結論としては、マニュアル読めということです。あと Devel::Leak::Object は簡単で便利。