share facebook facebook2 twitter menu hatena pocket slack

2014.10.28 TUE

”memory leak”が止まらない

sebastian

WRITTEN BYsebastian

cloudpackSebastianです。

AWS SDK for PHP がメモリ漏れてる〜〜〜〜
いや、マジで泣くかと思ったわ

Valgrindで確認してみた

Valgrindはメモリのリーク状態を確認出来る便利なツールね
結果が下の表のように返ります

項目 概要
definitely lost 間違いなくmemory leakを起こしているmemoryの値をbyte単位で出力する
indirectly lost pointer base構造上の主構造が消滅し取り残されたmemory leak 値をbyte単位で出力する
possibly lost Pointerにて上手く回避されて無いのであればmemory leakする可能性があるmemory leak値をbyte単位で出力する
still reachable 開放しなければmemory leakする可能性があるmemory。一般的には問題ない処理である。

何も実行してないPHP

要するにPHPのヘッダーだけのコード
本当に何もしてない

<?PHP

こいつをまずはValgrindでチェックしてみる

==2783== LEAK SUMMARY:
==2783==    definitely lost: 15,176 bytes in 1,067 blocks
==2783==    indirectly lost: 2,662 bytes in 44 blocks
==2783==      possibly lost: 0 bytes in 0 blocks
==2783==    still reachable: 81,456 bytes in 2,693 blocks
==2783==         suppressed: 0 bytes in 0 blocks

こんなん返ってきた
phpのbinaryだけでDefinnnitely lostが15,176 bytes、indirectory lostが2,662 bytes、合計で17,838 bytesのleak memoryが存在しているのが分かる。
このleak自体は実は問題が無い
こいつのleakは子Proccessでも何でも無いのでOS側のmemory managerがよしなに開放してくれちゃうからだ

AWS SDK for PHP を実行してみる

別に難しいことはしないで
SDKでdescribeInstancesを実行してEC2の情報と取りに行くだけのコードを実行してみる

<?php
require 'aws/aws-autoloader.php';
use AwsEc2Ec2Client;
use AwsCommonEnumRegion;

$objEc2Client = Ec2Client::factory(array('region' => Region::TOKYO));
$result = $objEc2Client->describeInstances();

phpのコード中にleak memoryが存在していないと何も実行していないphpと同じ内容になるはず!

でもね。これが結果なのよ

==2723== LEAK SUMMARY:
==2723==    definitely lost: 16,256 bytes in 1,071 blocks
==2723==    indirectly lost: 28,107 bytes in 83 blocks
==2723==      possibly lost: 0 bytes in 0 blocks
==2723==    still reachable: 165,793 bytes in 4,108 blocks
==2723==         suppressed: 0 bytes in 0 blocks

definnnitely lostが16,256 bytes、indirectory lostが28,107 bytes、44,363 bytes合計で存在している。
何も実行していないphpのmemory leak合計値が17,838 bytesなので差分値26,525 bytesがSDKの実行によって生じたmemory leakであると確認出来る訳ね

実際に組み込んで1時間動かした結果

1時間実行してgraphに書き出してみた
AWS SDK for PHP を、実際に組み込んで1時間動かした結果

ほ〜〜ら、usedがガンガン増えてfreeがめちゃくちゃ減ってる
これ、10秒に1回呼び出した結果ね
1時間で3600秒だから1時間で360回呼ばれてるわけだ
つまり、1年間稼働して1日1回SDK for phpの呼出が在るとして

365日×26,525byte=9,681,625 byte
1回のsdkの命令の呼出を毎日やるとして年間で10Mbyteほどのメモリがleakします。
実際は1回の呼出ではなく、各sdk内でhttps通信が発生する度に起きているので×n回分な訳だ

どこから漏れちゃってるの?

はい、ぶっちゃけると
AWS SDK for PHP 内でhttps通信に使われている Guzzleライブラリの中で呼び出しているlibCurlのphp extensionが使用しているlibnssライブラリの中です。
Guzzleライブラリはこの辺りは結構カバーしてて対策をとっているのだけど
対策が取られてるのって Guzzleの4系列で AWS SDK for PHP で使われているのは対策が取られていない3系列
で、この手のbugって結構、対策しても改修の度にまた再発って多いのでcurlやnssの動向は追っておこうね

そういう訳で定期的に呼び出す類のscriptはaws SDK for phpは避けた方が良いよん

元記事はこちらです。
”memory leak”が止まらない | 雲間を泳ぐ

sebastian

sebastian