MDB2
データベースからデータを書き出すテストプログラムを作成中に、盛大なメモリリーク?と思われるものに遭遇。
現段階ではほんの10Mbちょっと余計にメモリを食っているだけだが、データベースからの書き出し件数に従ってメモリ使用量が増えるようだと致命的なので色々と調べてみる。
使っているのはPHPのPEARライブラリのMDB2なのだが、ぱっと見間違いが見つからない。
どうしても分からなくて色々当たっている内に$GLOBALSを確認してみると…確認出力が終わらなくてPHPがタイムアウト(笑)
何がおかしいか調べてみると「_MDB2_databases」が異常にたくさんあることが分かる。試しにこれを強制的にunsetしてみると、リークしていると思った分が綺麗になくなったので、これが原因と分かる。
この部分は「データベースの接続」を記録しているはずなのだが、色々検索していると、「disconnect()してもこの情報は消えないのでさらにfree()としなければならない」事が分かる。
それにしても、データベース接続は最初の一度だけであとはその接続を使い回しているのにどうして…と思って調べてまわってみると、やっと原因が見つかった。
SQLの文字列をエスケープする際に、「空の接続」を使っていることが分かった。(接続先データベースとかが無くてもMDB2の接続オブジェクト?は作成できるので、データベースの種類だけ指定して接続を作ってエスケープ処理に使っていたのだった。)スコープが終了すればオブジェクトも消滅するので特に気にせずに書いていたのだが、まさかグローバル変数に記録を書いているとは…
空の接続を新規に作るのは止めて呼び出し側から接続を提供するように改め、ついでにfree()も呼び出すようにした。*1
結局、PHP自体の問題ではないし、邪魔になっていたのは本来なら使用される情報なので、これは「メモリリーク」とはいわないんだろうなぁ…。
幸いデータベースアクセスは完全にモジュール化していたので、そのモジュールの中だけで問題は解決した様子。とりあえず、異常なメモリ消費の増加はなくなった。(それでもプログラムの冒頭と最後で比べると数キロバイト増えているけど。他にもGLOBALに何か書いているプログラムとかいそうだし、普通に解除し忘れている変数とかあるのだろう。)
*1:接続一つ分の消費は少ないのでプログラム終了まで残しておいても問題ないが。