PHP 5.2 メモリマネージャー
?
uber-nerd のように PHP メモリを追跡および監視します
紹介: ?PHP V5.2 で導入された新しいメモリ マネージャーの使用方法を学び、メモリ使用量の追跡と監視に習熟し始めます。これにより、PHP V5.2 ではより多くのメモリをより効率的に使用できるようになります。
このシリーズのコンテンツをさらに見る
発売日:
?2007年4月10日
レベル:
?中級
訪問状況
?1145 ビュー
提案:
?0?(コメントを追加
)
平均スコア (合計 2 件の評価)
PHP V5.2: 開始
PHP V5.2 は 2006 年 11 月にリリースされ、多くの新機能とバグ修正が含まれていました。バージョン 5.1 は非推奨となり、すべての人に推奨されます。 PHP V5 ユーザーはアップグレードします。私のお気に入りのラボ環境 -- Windows®、Apache、MySQL、PHP (WAMP) -- が導入されました V5.2 の新しいパッケージに含まれています (「リソース」を参照) )。そこには、Windows® XP または 2003 コンピュータに PHP V5.2、MySQL、および Apache をインストールするためのアプリケーションがあります。インストールは非常に簡単で、管理上の利点がたくさんあるので、使用することを心からお勧めします。
これは Windows ユーザーにとって最も単純なパッケージですが、Linux で PHP を構成する場合は、次のコードを追加する必要があります: --memory-limit-enabled
(サーバーで利用可能な他のオプションに加えて)。しかし、Windowsではこの問題を解決する機能が提供されています。
PHP V5.2 には多くの改良点があり、重要な領域の 1 つはメモリ管理です。 README.ZEND_MM からの正確な引用: 「新しいメモリ マネージャー (PHP5.2 以降) の目標は、メモリ割り当てのオーバーヘッドを削減し、メモリ管理を高速化することです。」
V5.2 リリース ノートから重要なポイントをいくつか示します:
--disable-zend-memory-manager
構成オプション--enable-malloc-mm
ビルドのデバッグ時にデフォルトで有効になり、内部および外部メモリ デバッガーの使用を許可する構成オプション ZEND_MM_MEM_TYPE
そしてZEND_MM_SEG_SIZE
環境変数調整メモリマネージャーこれらの新しい追加が何を意味するかを理解するには、メモリ管理の技術を掘り下げ、割り当てのオーバーヘッドと実行速度がなぜ大きな問題になるのかを考える必要があります。
トップに戻る
なぜメモリ管理を行うのでしょうか?
コンピューティング分野で最も急速に発展しているテクノロジーの 1 つはメモリとデータ ストレージであり、速度とストレージ サイズの増大が継続的に求められています。初期のコンピューターの使用 カードはメモリとして機能し、その後チップ技術に移行しました。たった 1 KB の RAM で想像できますか メモリコンピュータの動作シナリオ?初期のコンピューター プログラマーの多くがそれを使用しました。これらの先駆者たちは、テクノロジーの制限内で作業するには、些細なコマンドによるシステムの過負荷を慎重に回避する必要があることにすぐに気づきました。 負荷。
PHP 開発者であり、C++ を使用する 私たちは、他のより厳密な言語でコーディングする同僚よりもコーディングが容易な環境にいます。私たちの世界では、PHP があるため、私たち自身がシステム メモリをどうするかについて心配する必要はありません。 この問題は私たちに代わって処理します。ただし、他のプログラミング分野では、責任あるコーダーはさまざまな関数を使用して、実行されたコマンドが他のプログラム データを上書きしないようにします。 したがって、プログラムの動作は破壊されます。
メモリ管理は通常、コーダーからのメモリ ブロックの割り当てと解放のリクエストによって処理されます。 割り当てブロック あらゆる種類のデータを保存でき、このプロセスにより、そのデータ用に一定量のメモリが確保され、操作でデータが必要になったときにアプリケーションがデータにアクセスするためのメソッドが提供されます。プログラムは、操作の完了後に割り当てられたメモリを解放し、システムや他のプログラマがそのメモリを使用できるようにすることが期待されます。プログラムがメモリをシステムに解放しない場合、それはメモリ リークと呼ばれます。 。
リークは、実行中のプログラムに共通する問題であり、特に実行中のプログラムが即座に終了し、デフォルトでプログラムに割り当てられているすべてのメモリが解放されることがわかっている場合には、通常はある程度許容できます。
ほとんどすべてのクライアント アプリケーションと同様、これはプログラムがランダムに実行および終了することによる問題です。サーバー アプリケーションが終了または再起動することなく無期限に実行されることが期待される 動的であるため、メモリ管理はサーバー デーモン プログラミングにとって非常に重要になります。長時間実行されるプログラムでは、メモリ ブロックが使用され、メモリ ブロックが使用されるため、たとえ小さなリークでも、最終的にはシステムを劣化させる問題に発展します。 決して解放されることはありません。
トップに戻る
長期的な考慮事項
他の言語で記述する場合と同様、PHP で記述された永続サーバー デーモンにはさまざまな用途があります。ただし、これらの目的で PHP を使用し始める場合は、メモリ使用量も考慮する必要があります。
大量のデータを解析したり、無限ループを隠したりするスクリプトは、大量のメモリを消費する傾向があります。明らかに、メモリが使い果たされるとサーバーのパフォーマンスが低下するため、スクリプトを実行するときに このとき、メモリ使用量にも注意する必要があります。システム モニターを有効にすることでメモリの使用状況を簡単に観察できますが、システム全体のメモリの状態以上に有用な情報は得られません。持っている コンテンツのトラブルシューティングや最適化を支援する以上のことが必要な場合もあれば、単に詳細が必要な場合もあります。
スクリプトの実行内容を透過的に把握する 1 つの方法は、内部または外部のデバッガーを使用することです。 内部デバッガ スクリプトを実行しているように見えるプロセスと同じです。オペレーティング システムの観点から考慮された独立したプロセスは、外部デバッガーです。 。デバッガを使用したメモリ分析はどちらの場合にも似ていますが、メモリにアクセスする方法が異なります。内部デバッガは実行中のプロセスが配置されているメモリ空間に直接アクセスできますが、外部デバッガはソケットを通じてメモリにアクセスします。
開発を支援するために使用できるメソッドや利用可能なデバッグ サーバー (外部) およびライブラリ (内部) が多数あります。デバッグ用に PHP インストールを準備するには、新しく提供された --enable-malloc-mm
を使用できます。
、DEBUG
にあります
ビルドではデフォルトで有効になります。これにより、環境変数 USE_ZEND_ALLOC
が作成されます。
実行時に malloc の選択を許可するために使用できます。
または emalloc
メモリの割り当て。 malloc タイプのメモリ割り当てを使用すると、外部デバッガでメモリの使用状況を監視できるようになりますが、emalloc 割り当てでは Zend メモリ マネージャの抽象化が使用されるため、内部デバッグが必要になります。
トップに戻る
PHP のメモリ管理関数
メモリマネージャーをより柔軟かつ透過的にすることに加えて、PHP V5.2 は memory_get_usage()
も提供します。
そしてmemory_get_peak_usage()
これら 2 つの関数に新しいパラメータが提供され、メモリ使用量を表示できるようになりました。説明に記載されている新しいブール値は real_size
です。
。関数 memory_get_usage($real);
を呼び出すことで
(ここで $real = true
)、結果は、メモリ マネージャーのオーバーヘッドを含む、呼び出し時にシステムに実際に割り当てられたメモリのサイズになります。タグ グループを使用しない場合、返されるデータには、実行中のスクリプト内で使用されたメモリからメモリ マネージャーのオーバーヘッドを除いたもののみが含まれます。
memory_get_usage()
そしてmemory_get_peak_usage()
違いは、後者は呼び出し元の実行プロセスに対してこれまでに最高量のメモリを返すのに対し、前者は実行時に使用された量のみを返すことです。
memory_get_usage()
向け
php.net では、リスト 1 のコード スニペットを提供しています。
memory_get_usage()
例
<?php // This is only an example, the numbers below will // differ depending on your system echo memory_get_usage() . "\n"; // 36640 $a = str_repeat("Hello", 4242); echo memory_get_usage() . "\n"; // 57960 unset($a); echo memory_get_usage() . "\n"; // 36744 ?> |
在这个简单示例中,我们首先回转了直接调用 memory_get_usage()
的结果,代码注释中显示可能在作者的系统中有 36640 字节的常见结果。然后我们使用 4,242 个 “Hello” 副本来装载 $a
并再次运行函数。图 1 中可以看到此简单应用的输出。
memory_get_usage()
的示例输出
没有 memory_get_peak_usage()
的示例,因为两者十分相似,语法是相同的。但是,对于清单 1 中的示例代码,将只有一个结果,即当时的最高内存使用量。让我们看一看清单 2。
memory_get_peak_usage()
示例
<?php // This is only an example, the numbers below will // differ depending on your system echo memory_get_peak_usage() . "\n"; // 36640 $a = str_repeat("Hello", 4242); echo memory_get_peak_usage() . "\n"; // 57960 unset($a); echo memory_get_peak_usage() . "\n"; // 36744 ?> |
清单 2 中的代码跟图 1 一样,但是 memory_get_usage()
已经替换为 memory_get_peak_usage()
。在我们用 4242 个 “Hello” 副本填充 $a
之前,输出都不会有多大更改。内存跳升至 57960,表示到目前为止的峰值。当检查内存使用量峰值时,得到了目前为止的最高值,因此所有进一步调用都将得到 57960,直至我们处理的操作比处理 $a
使用的内存更多(参见图 2)。
memory_get_peak_usage()
的示例输出
回页首
限制内存使用
确保托管应用程序的服务器不过载的一种方法是限制 PHP 执行的任何脚本使用的内存量。这根本不是我们应当执行的操作,但由于 PHP 是一种松散类型的语言,并且是在运行时解析的,因此我们有时会获得在释放到生产应用程序中后编写得很差的脚本。这些脚本可能执行循环,也可能打开一张长的 文件列表,忘记在打开新文件之前先关闭当前文件。无论在哪一种情况下,编写很差的脚本可能在您知道之前以消耗大量内存告终。
在 PHP.INI 中,您可以使用配制参数 memory_limit
来指定任何脚本能够在系统中运行的最大内存使用量。这不是对于 V5.2 的特定更改,但是内存管理器及其使用的任何讨论都值得至少快速查看一次这个特性。它还精心地引导我使用内存管理器的最后几个新功能:环境变量。
回页首
调整内存管理器
最后,在不能做完美主义者但是又完全符合自己目的的情况下怎样编程?新环境变量 ZEND_MM_MEM_TYPE
和 ZEND_MM_SEG_SIZE
正好可以满足您的需求。
当内存管理器分配大型内存块时,它是安装 ZEND_MM_SEG_SIZE
变量中列出的预定大小执行操作的。这些内存块的默认分区大小为每块 256
KB,但是您可以调整这些分区大小以满足特殊需求。例如,如果您注意到最常用的一个脚本中的操作导致大量的内存浪费,则可以将此大小调整为更接近匹配脚本
需求的值,减少分配的内存量但剩下的内存量仍然为零。在正确的条件下,此类谨慎的配制调整可能造成巨大差别。
回页首
在 Windows 中检索内存使用情况
如果具有预构建的 PHP Windows 二进制代码,而没有在构建时使用 --enable-memory-limit
选项,则需要先浏览此部分然后再继续。对于 Linux?,配置 PHP 构建时用 --enable-memory-limit
选项构建 PHP。
要使用 Windows 二进制代码检索内存使用情况,请创建以下函数。
<?php function memory_get_usage(){ $output = array(); exec('tasklist /FI "PID eq '.getmypid().'" /FO LIST', $output ); return preg_replace( '/[^0-9]/', '', $output[5] ) * 1024; } ?> |
将结果保存到名为 function.php 的文件。现在您只能将此文件包含在需要使用它的脚本中。
回页首
动手实践
让我们来看一看使用这些设置的实际示例给我们带来的好处。可能有很多次您都想知道为什么在脚本的末尾没有正确分配内存。原 因是因为一些函数本身导致了内存泄露,尤其是在仅使用内置 PHP 函数的情况下。在这里,您将了解如何发现此类问题。并且为了开始进行内存泄露查找的征战,您将创建一个测试 MySQL 数据库,如清单 4 所示。
mysql> create database memory_test; mysql> use memory_test; mysql> create table leak_test ( id int not null primary key auto_increment, data varchar(255) not null default ''); mysql> insert into leak_test (data) values ("data1"),("data 2"), ("data 3"),("data 4"),("data 5"),("data6"),("data 7"), ("data 8"),("data 9"),("data 10"); |
这将创建一个带有 ID 字段和数据字段的简单表。
在下一张清单中,想象我们坚韧不拔的程序员正在执行一些 MySQL 函数,特别是使用 mysql_query()
将结果应用到变量。当他这样做时,他将注意到即使调用 mysql_free_result()
,一些内存也不会被释放,导致内存使用量随着 Apache 进程不断增长(参见清单 5)。
for ( $x=0; $x<300; $x++ ) { $db = mysql_connect("localhost", "root", "test"); mysql_select_db("test"); $sql = "SELECT data FROM test"; $result = mysql_query($sql); // The operation suspected of leaking mysql_free_result($result); mysql_close($db); } |
清单 5 是在任何位置都可能使用的简单 MySQL 数据库操作。在运行脚本时,我们注意到一些与内存使用量相关的奇怪行为并需要将其检查出来。为了使用内存管理函数以使我们可以检验发生错误的位置,我们将使用以下代码。
<?php if( !function_exists('memory_get_usage') ){ include('function.php'); } echo "At the start we're using (in bytes): ", memory_get_usage() , "\n<br>"; $db = mysql_connect("localhost", "user", "password"); mysql_select_db("memory_test"); echo "After connecting, we're using (in bytes): ", memory_get_usage(),"\n<br>"; for ( $x=0; $x<10; $x++ ) { $sql = "SELECT data FROM leak_test WHERE id='".$x."'"; $result = mysql_query($sql); // The operation // suspected of leaking. echo "After query #$x, we're using (in bytes): ", memory_get_usage(), "\n<br>"; mysql_free_result($result); echo "After freeing result $x, we're using (in bytes): ", memory_get_usage(), "\n<br>"; } mysql_close($db); echo "After closing the connection, we're using (in bytes): ", memory_get_usage(), "\n<br>"; echo "Peak memory usage for the script (in bytes):". memory_get_peak_usage(); ?> |
注:按照定义的时间间隔检查当前内存使用量。在下面的输出中,通过显示我们的脚本一直在为函数分配内存,并且在应当释放的时候没有释放内存,从而提供对内存泄露的实际测试,您可以看到每次调用时内存使用量如何增长。
At the start we're using (in bytes): 63216 After connecting, we're using (in bytes): 64436 After query #0, we're using (in bytes): 64760 After freeing result 0, we're using (in bytes): 64828 After query #1, we're using (in bytes): 65004 After freeing result 1, we're using (in bytes): 65080 After query #2, we're using (in bytes): 65160 After freeing result 2, we're using (in bytes): 65204 After query #3, we're using (in bytes): 65284 After freeing result 3, we're using (in bytes): 65328 After query #4, we're using (in bytes): 65408 After freeing result 4, we're using (in bytes): 65452 After query #5, we're using (in bytes): 65532 After freeing result 5, we're using (in bytes): 65576 After query #6, we're using (in bytes): 65656 After freeing result 6, we're using (in bytes): 65700 After query #7, we're using (in bytes): 65780 After freeing result 7, we're using (in bytes): 65824 After query #8, we're using (in bytes): 65904 After freeing result 8, we're using (in bytes): 65948 After query #9, we're using (in bytes): 66028 After freeing result 9, we're using (in bytes): 66072 After closing the connection, we're using (in bytes): 65108 Peak memory usage for the script (in bytes): 88748 |
私たちが行ったのは、スクリプトの実行時に発生したいくつかの不審な操作を見つけて、わかりやすいフィードバックが得られるようにスクリプトを調整することでした。各反復中に memory_get_usage()
を使用してスクリプトを再度実行しました。
メモリ使用量の変化を表示します。割り当てられたメモリ値の増加に基づいて、スクリプトを使用してどこかに脆弱性を確立したことが示唆されます。 mysql_free_result()
のせいで
この関数はメモリを解放しないので、mysql_query()
と考えることができます。
メモリが正しく割り当てられていません。
トップに戻る
結論
PHP V5.2 には、スクリプトのシステム メモリ割り当てをより詳細に把握し、メモリ管理の微調整を完全に制御できるようにするための優れた新しいツールがいくつか含まれています。新しいメモリ管理ツールを効果的に使用すると、デバッグ作業がサポートされ、システム リソースの一部を取り戻すことができます。
参考文献
学ぶ
製品とテクノロジーを入手