質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.92%

memory leaks detected

解決済

回答 3

投稿

  • 評価
  • クリップ 2
  • VIEW 1,029

hotta

score 1598

PHP Extension を書いています。ターゲットは php-7.1.4(Zend Engine 3.1)です。質問に "Zend Engine" タグを付けようとしましたが、タグが未登録のようです(追加要望を出しておきました)。

一応必要最小限は動くようになったので、サンプルスクリプトを php で書いて実行してみましたが、以下のような表示が出ました。いずれも自作 Extension の中で eXXalloc() したものです。

Fri May 26 13:43:46 2017]  Script:  '/home/vagrant/temp/b.php'
/home/vagrant/php/ext/my_ext/my_ext.c(326) :  Freeing 0x00007f7dd6402fc0 (25 bytes), script=/home/vagrant/temp/b.php
[Fri May 26 13:43:46 2017]  Script:  '/home/vagrant/temp/b.php'
/home/vagrant/php/ext/my_ext/my_ext.c(256) :  Freeing 0x00007f7dd6472028 (8 bytes), script=/home/vagrant/temp/b.php
=== Total 2 memory leaks detected ===

gdb で追ってみると、PHP スクリプト的にはすでに実行が終わっていて、いわゆるクリーンアップ処理で出ているようです。

1492                                            snprintf(memory_leak_buf, 512, "%s(%d) :  Freeing " ZEND_ADDR_FMT " (%zu bytes), script=%s\n", t->filename, t->lineno, (size_t)t->addr, t->size, SAFE_FILENAME(SG(request_info).path_translated));
(gdb) bt
#0  php_message_handler_for_zend (message=4, data=0x7fffffffcc60)
    at /usr/local/src/php-7.1.4/main/main.c:1492
#1  0x00000000008520b4 in zend_message_dispatcher (message=4,
    data=0x7fffffffcc60) at /usr/local/src/php-7.1.4/Zend/zend.c:1030
#2  0x000000000081c181 in zend_mm_check_leaks (heap=0x7ffff5e00040)
    at /usr/local/src/php-7.1.4/Zend/zend_alloc.c:2096
#3  0x000000000081c4b9 in zend_mm_shutdown (heap=0x7ffff5e00040, full=0,
    silent=0) at /usr/local/src/php-7.1.4/Zend/zend_alloc.c:2168
#4  0x000000000081d20f in shutdown_memory_manager (silent=0, full_shutdown=0)
    at /usr/local/src/php-7.1.4/Zend/zend_alloc.c:2591
#5  0x00000000007c1b28 in php_request_shutdown (dummy=0x0)
    at /usr/local/src/php-7.1.4/main/main.c:1900
#6  0x0000000000933577 in do_cli (argc=2, argv=0x10e1b60)
    at /usr/local/src/php-7.1.4/sapi/cli/php_cli.c:1160
#7  0x0000000000933c5b in main (argc=2, argv=0x10e1b60)
    at /usr/local/src/php-7.1.4/sapi/cli/php_cli.c:1381

ここを表示しているソース(main/main.c)を見ると、出力している箇所全体が "#if ZEND_DEBUG" で囲まれていました。ZEND_DEBUG は、親分の PHP バイナリをビルドする際の configure で --enable-debug を付けると有効になるようです。試しに --enable-debug を付けないでやると、コンパイラに -DZEND_DEBUG が渡されず、実行しても "Freeing ..." が出ないことを確認できました。

ものの本(*1)によると、emalloc() は malloc() のラッパーであり、明示的に free() しなくても Zend Engine が自動的に未解放エリアを検知して開放してくれる仕組みのようですので、プログラマ的には気にしなくてもよいものだと思っているのですが、この認識で正しいでしょうか?

また、ZE3 向けの Extension 開発において、参考になるサイトや書籍等がありましたらご紹介いただけるとありがたいです。下記の本は(アマゾンの評価にも書きましたが)10年前の本なので、当然 PHP7 への言及はありません。

(*1) Extending and Embedding PHP

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+1

Facebookで簡単に回答したのですが、こちらでも。PHP7のメモリ管理の説明で、5.xでは異なります。

基本的にはemalloc(やその仲間、ecalloc/estrdupなど)で割り当てたメモリはefreeが必要です。例外はPHPが管理するメモリになった場合です。この場合、メモリは自動的に解放されます。

  • シンボルテーブルに登録されたPHP変数(zvalでPHPスクリプトから変数として利用できる物)
  • PHPのリソースとして登録された変数(例えば、pgsqlの場合、接続リソース)

zval変数を解放する場合はzval_dtorなどを使います。これを利用するとzvalの中に何が入っていても、文字列でも配列でもオブジェクトでも、正しいデストラクタが呼ばれて綺麗に解放されます。

モジュール内で一時的に使うzval *やchar *などを作った場合、efreeが必要になります。(zvalの場合、普通はzvalポインタ変数にしてemallocするのではなく、zval変数にしてzval自体のメモリは解放しなくても良いようにします。しかし、できない場合もあるのでその場合は解放します)

PHP7からはzval構造体の文字列としてzend_stringが使われます。zend_stringはefreeしてはなりません。間違えやすいので注意してください。 基本的にはzend_string_releaseを利用して解放します。(zend_string_freeは少し違う動作をします。コードを参照してください)zend_stringでもメモリリークエラーが発生します。

PHPのCモジュール内ではchar *とzend_string *が使えますが、zend_string *が使える場合はこちらを使う方が良いです。zend_string型の変数はzend_string_init/allocなどで初期化します。

配列の場合、array_initなどを使いますが、これも同じメモリリークエラーを発生させます。zvalの中に入れた配列(ハッシュ)ならzval_dtorなどで解放できます。生の配列(ハッシュ)の場合(zend_hash_initなど)はzend_hash_destroy/cleanを利用します。

デバッグビルドで発生したメモリリークエラーは全て修正しなければなりません。PHPは自分(PHP)が管理しているメモリのみ自動解放します。それ以外はプログラマが解放する必要があります。

PHPのAPIを使っている限り、通常のmallocなどは普通は使われません。この為、メモリリークはあまり気にしなくても、メモリリークはテストコードで気付けます。ただし、zvalの文字列型や配列型は参照カウンタ(refcount)を持っているので、不用意にこれをインクリメントするとPHPスクリプト実行が終るまで解放されません。PHPスクリプトの実行終了までメモリが無駄に消費されることになるので注意してください。

メモリ管理は大きなテーマなので以上では全く足りていませんが、メモリリークエラーの対応の手始めとしては基本的な情報は書いたと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

checkベストアンサー

0

emalloc/efree に関しては大垣さんが facebook でコメントしていたので、別アプローチで回答してみます。

PHP Extension をより簡単に開発する手法としては Zephir 言語を使うというアプローチもあります。

https://zephir-lang.com/

Zephir は Phalcon フレームワークの開発者達が作った PHP Extension を開発するための専用言語で、C言語とPHPの中間のような言語仕様です。もちろん PHP7 に対応しています。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/27 17:26 編集

    リポジトリ見たら、枯れているような感じじゃないですが、かなり活発みたいですね。ということは、最新の PHP に追随(しようと)してるんでしょうね。

    Zend Engine のソースにあまりにもコメントがなく、ドキュメントもないのでみんなどうしてるんだろうと思ってましたが、こっちに集まってるのかな。ドキュメントがあるというのがかなり大きいです。

    今やっているのは大きいプロジェクトのごく一部だし、プロジェクトは始まったばかりなので、今から Zephir で再実装しても十分元が取れるような気がしてきました。

    キャンセル

  • 2017/05/29 13:34

    ドキュメントをざっとを読んでみました。言語仕様としては非常に魅力的で、PHP Module の細かい規約等を意識せずに、C よりははるかに楽にコードを書けそうな気がします。

    ただ残念なことに、今回 Extension にした最大の理由である、外部ライブラリ内にある C 関数の呼び出しには、言語レベルでは対応していなさそうです。

    %{ // c-code }% という書式で C のインライン読み込みをサポートしているようですので、あとはインライン部分と Zephir 部分でのデータのやり取りについて試行錯誤してみます。

    https://stackoverflow.com/questions/25797337/is-it-possible-with-zephir-to-include-an-external-library

    キャンセル

  • 2017/05/30 10:31

    外部 C コードの呼び出しで最も推奨されるのは optimizer を使う方法らしいです。ただ、残念ながらこの方式では元の C ソースが必要らしいので断念しました(今回のケースではソースはありません)。インライン文法だと、結局 C 関数の呼び出し前後のメモリ管理を C 側でやらないといけなくなるので、Zephir のメリットが薄れてしまいます。

    キャンセル

  • 2017/05/30 15:22

    なるほど、この辺はよく理解してなかったのであらためて調べてみました。
    Optimizer の中では結局Cのコードを出力するので、そこをうまくやれば外部ライブラリのC関数を呼び出すことはできそうです。しかしおっしゃるように、ポインタ等各種値のZVALへのマッピングや、メモリの確保/開放処理はCで書くのと同程度の処理を書かなければいけないので、Zephier でやるメリットは薄そうですね。

    https://github.com/treffynnon/Benchmark-PHP-HHVM-Zephir/tree/master/cli-php-zephir-optimizer

    キャンセル

0

Facebook 側で回答いただきましたが、デバッグビルドで警告が出るものは、やはり対処が必要とのことです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.92%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る