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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

3回答

1921閲覧

memory leaks detected

hotta

総合スコア1613

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

2クリップ

投稿2017/05/26 05:46

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

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

bash

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

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

bash

11492 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)); 2(gdb) bt 3#0 php_message_handler_for_zend (message=4, data=0x7fffffffcc60) 4 at /usr/local/src/php-7.1.4/main/main.c:1492 5#1 0x00000000008520b4 in zend_message_dispatcher (message=4, 6 data=0x7fffffffcc60) at /usr/local/src/php-7.1.4/Zend/zend.c:1030 7#2 0x000000000081c181 in zend_mm_check_leaks (heap=0x7ffff5e00040) 8 at /usr/local/src/php-7.1.4/Zend/zend_alloc.c:2096 9#3 0x000000000081c4b9 in zend_mm_shutdown (heap=0x7ffff5e00040, full=0, 10 silent=0) at /usr/local/src/php-7.1.4/Zend/zend_alloc.c:2168 11#4 0x000000000081d20f in shutdown_memory_manager (silent=0, full_shutdown=0) 12 at /usr/local/src/php-7.1.4/Zend/zend_alloc.c:2591 13#5 0x00000000007c1b28 in php_request_shutdown (dummy=0x0) 14 at /usr/local/src/php-7.1.4/main/main.c:1900 15#6 0x0000000000933577 in do_cli (argc=2, argv=0x10e1b60) 16 at /usr/local/src/php-7.1.4/sapi/cli/php_cli.c:1160 17#7 0x0000000000933c5b in main (argc=2, argv=0x10e1b60) 18 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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

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スクリプトの実行終了までメモリが無駄に消費されることになるので注意してください。

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

投稿2017/05/27 08:47

編集2017/05/27 08:58
YasuoOhgaki

総合スコア51

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

投稿2017/05/27 08:39

hotta

総合スコア1613

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

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

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

https://zephir-lang.com/

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

投稿2017/05/27 07:16

koyhoge

総合スコア268

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hotta

2017/05/31 00:10 編集

リポジトリ見たら、枯れているような感じじゃないですが、かなり活発みたいですね。ということは、最新の PHP に追随(しようと)してるんでしょうね。 Zend Engine のソースにあまりにもコメントがなく、ドキュメントもないのでみんなどうしてるんだろうと思ってましたが、こっちに集まってるのかな。ドキュメントがあるというのがかなり大きいです。 今やっているのは大きいプロジェクトのごく一部だし、プロジェクトは始まったばかりなので、今から Zephir で再実装しても十分元が取れるような気がしてきました。
hotta

2017/05/29 04: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
hotta

2017/05/30 01:31

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

2017/05/30 06:22

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問