C では、malloc と free はメモリの割り当てと解放に直接使用されますが、メモリの割り当てと解放が頻繁に行われると、メモリの断片が生成され、PHP 変数の割り当てと解放が頻繁に行われます。言語レベルのアプリケーションとして、この損失は許容できないため、PHP は独自のメモリ プール ZendMM を実装して、glibc の malloc を置き換え、頻繁なメモリの割り当てと解放の問題を解決しました。
チャンク、ページ、スロットの 3 つの粒度でメモリ操作を定義します。各チャンクのサイズは 2MB、各ページは 512 ページに分割され、申請時に各ページが複数のスロットに分割されます。メモリ、アプリケーションのサイズに応じてさまざまな割り当て戦略が実装されます
huge: 2MB を超え、システム割り当てを直接呼び出し、いくつかのチャンクを割り当てます
Large: 3092B (3/4 ページ) より大きいメモリ、2044kb (511 ページ) 未満のメモリを適用します)、複数のページを割り当てます
small: 要求されたメモリが 3092B 未満の場合、メモリ プールは事前に 30 の異なるサイズのメモリ (8、16、32...、3072) を定義しており、それらは異なるページに分散されます。メモリを適用する場合、対応するメモリを直接検索すると、メモリ プールには、大規模なメモリ リンク リスト、チャンク リンク リスト、スロット内のさまざまなサイズのメモリ リンク リストなど、zend_mm_heap 構造を通じてメモリ プールの主要な情報が格納されます。
大きなメモリは実際には複数のチャンクに割り当てられ、zend_mm_huge_list 構造体を通じて管理されます。 大きなメモリ それらの間で一方向のリンク リストが形成されます。
チャンクは、システムにメモリを適用および解放するためのメモリ プールの最小粒度です。チャンク間で双方向のリンク リストが形成され、最初のチャンクのアドレスが zend_mm_heap->main_chunk に格納され、最初のページのメモリはポインタなどのチャンク自体の構造体メンバーの保存に使用されます。前と次のチャンクの、現在のチャンクの各ページの使用状況など。
同じサイズのスロットは、単一リンクされたリストを形成します。
メモリ プールの初期化は、php_module_startup 段階で完了します。初期化プロセスでは、主に割り当てが行われます。ヒープ構造の場合、このプロセスは start_memory_manager プロセスで完了します。マルチスレッド環境の場合、スレッドは相互に影響しません。非スレッドのメモリ ヒュージ ページを有効にするかどうかを設定するために使用されます。安全な環境では、割り当てられたヒープは AG マクロである alloc_globals に保存されます。zend_mm_heap 構造体は個別に割り当てられるのではなく、チャンク構造はページを占有するため、実際にはそれほど大きなメモリを使用しません。そのため、スペースを最大限に活用するために、ここに配置します
メモリ割り当て: 1. 巨大なメモリ割り当て2MB を超える場合は、n 個のチャンクにアライメントされて割り当てられます。zned_mm_huge_list 構造は、すべての巨大なメモリを管理します。メモリ アライメント プロセスは、オペレーティング システムによって単純に完了するのではなく、適用後にメモリ プール自体によって調整されます。メモリのアライメントが達成できる場合は、調整する必要はなく、直接使用するように戻ります。ZEND_MM_CHUNK_SIZE (2MB) の整数倍でない場合、zendMM はこのメモリを解放し、実際のメモリ サイズ + ZEND_MM_CHUNK_SIZE に従って再度適用します。追加のブロックは調整 2 に使用されます。大規模割り当て: 要求されたメモリ サイズが 3072B (3/4 ページ) ~ 2044k (511 ページ) の場合、メモリ プールはチャンク上で対応するページ数を検索して返します。ラージのアプリケーション粒度はページであり、チャンクには free_map と map の 2 つのメンバーがあり、ページ割り当て情報を記録するために使用されます
free_map は、チャンク上のページの割り当てを記録するために使用されます。 1 に設定されます。マップは、ページの割り当てタイプと割り当てられたページの数を記録するために使用されます。最上位 2 桁は、ページの割り当てタイプを記録します。01 は大きいです。割り当て時には、最初のチャンクから走査を開始し、そのチャンクに要件を満たすページがあるかどうかを確認し、適切なページがない場合は次のチャンクを検索します。最後に、チャンクを再割り当てします。誰が十分なページを見つけたかはわかりませんが、できるだけチャンクの隙間を埋め、割り当てられたページが互いに一致するようにしてください。中間のページ ギャップを避けるため (その後の割り当て中の検索数を減らすため) 1) 最初のチャンク グループ (0 ~ 63) からチェックを開始します。現在のグループに使用可能なページがある場合は、最初にチャンク グループのビットをチェックします。現在のページ、最初と最後の空きページの場所を見つけます。十分でない場合は、これらのページを 1 (割り当て済み) としてマークし、他のグループを検索します。ページが適切であれば、それを直接使用し、検索を中断します。ページが必要なサイズより大きい場合は、使用可能ではあるが最適ではないことを示します。その後、最終的に比較して最適なチャンク (ページを最大限に活用できるチャンク) が見つかるまで、他のチャンクが検索されます。 2) 適切なページが見つかった後。 、対応するページ情報、つまりfree_mapとmapを設定します。情報はページアドレスに戻ります。 3. 小さな割り当て:
対応する仕様のメモリが割り当てられているかどうかを最初に確認します。割り当てられていない場合は、割り当てが使い果たされると、ページの割り当てプロセスは大きな割り当てと一致し、固定サイズに従ってスロットに分割されます。単一のリンク リストの先頭は AG (mm_heap)->free_slot に保存されます
メモリ プールの解放の粒度はチャンクであり、efree 1 によって完了します。巨大なメモリ、大規模なメモリ、および小規模なメモリ タイプの解放は、チャンクの最初のページが占有されているため、オフセットを 0 にすることは不可能です。チャンクのタイプと大小のタイプを解放するとき、占有されているチャンクは同時に解放され、AG リンク リストから削除されます。 2. 大メモリの解放: 計算されたオフセットの場合。が 0 ではない場合、アドレスが大きいか小さいメモリであることを意味し、オフセットに従ってページ番号が計算されます。ページを取得した後、チャンク -> マップからページの割り当てタイプを取得できます。 Large は、指定されたタイプのメモリを直接解放しませんが、解放後にチャンクが未割り当てであることが判明した場合、そのチャンクを解放します。キャッシュ数が特定の値に達すると、新しく追加されたチャンクはキャッシュされなくなり、システムは、キャッシュされたチャンクが大量のメモリを占有することを避けます。 3. 小さいタイプのリリースの場合、解放されたスロットは、ルール スロットの利用可能なリンク リストに直接挿入されます。比較的簡単です
C では、malloc と free はメモリの割り当てと解放に直接使用されますが、メモリの割り当てと解放が頻繁に行われると、メモリの断片が生成され、PHP 変数の割り当てと解放が頻繁に行われます。言語レベルのアプリケーションとして、この損失は許容できないため、PHP は独自のメモリ プール ZendMM を実装して、glibc の malloc を置き換え、頻繁なメモリの割り当てと解放の問題を解決しました。チャンク、ページ、スロットの 3 つの粒度のメモリ操作を定義します。各チャンクのサイズは 2MB、各ページは 4kB です。メモリを適用する場合、各ページは複数のスロットに分割されます。要求されたサイズに従って実装されます
巨大: 2MB より大きい、システム割り当てを直接呼び出し、いくつかのチャンクを割り当てます
大: 3092B (3/4 ページ) より大きいメモリを適用し、2044kb (511 ページ) より小さい、複数のページを割り当てます
小: 3092B 未満のメモリを適用します。メモリ プールには、事前に 30 個の異なるサイズのメモリ (8、16、32...、3072) が定義されており、メモリを適用するときに、それらは対応するメモリ上に直接分散されます。対応するスロットを見つけます。 メモリ プールには、大規模メモリ リンク リスト、チャンク リンク リスト、各スロット サイズのメモリ リンク リストなど、メモリ プールの主要な情報が zend_mm_heap 構造体を介して保存されます。
大きなメモリは実際には複数のスロットに割り当てられます。 zend_mm_huge_list 構造体を通じてチャンクが管理されると、大規模なメモリ間で一方向リンク リストが形成されます
チャンクは、メモリをシステムに適用および解放するためのメモリ プールの最小粒度です。チャンク間で二重リンク リストが形成されます。最初のチャンクのアドレスは、最初のページである zend_mm_heap->main_chunk に保存されます。メモリは、前部チャンクと後部チャンクのポインター、現在のチャンクの各ページの使用状況など、チャンク自体の構造メンバーを保存するために使用されます。
同じサイズのスロットは単一リンクされたリストを形成します
メモリプールの初期化は php_module_startup ステージで完了し、初期化プロセスは主にヒープ構造の割り当てであり、このプロセスは start_memory_manager プロセスで完了します。マルチスレッド環境の場合、メモリ プールはスレッドごとに割り当てられ、初期化中にメモリ ヒュージ ページを有効にするかどうかは環境変数 use_zend_alloc_huge_pages に従って設定されます。非スレッドセーフ環境では、割り当てられたヒープは AG マクロである alloc_globals に保存されます。チャンク構造はページを占有するため、zend_mm_heap 構造は個別に割り当てられるのではなく、チャンク構造に埋め込まれることに注意してください。 . しかし、実際には、スペースを最大限に活用するために、ここに配置します
メモリ割り当て: 1. 2MB を超える巨大なメモリ割り当ては、割り当て時に n 個のチャンクにアライメントされます。 zned_mm_huge_list 構造体もすべての巨大なメモリを管理するために割り当てられます。メモリ アライメント プロセスは、オペレーティング システムによって単純に完了するのではなく、最初に実際のサイズに適用され、適用後にメモリ プール自体によって調整されます。メモリに到達するだけです。調整する必要はありません。ZEND_MM_CHUNK_SIZE (2MB) の整数倍でない場合、zendMM はこのメモリを解放し、実際のメモリ サイズ + ZEND_MM_CHUNK_SIZE に従って再度適用します。アプリケーションの追加部分は調整に使用されます。 2. 大規模な割り当て: 適用されたメモリ サイズが 3072B (3/4 ページ) ~ 2044k (511 ページ) の場合、メモリ プールはチャンク上で対応するページ数を検索します。大きいアプリケーション粒度はページであり、free_mapとmapの2つのメンバーがページ割り当て情報を記録するために使用されます
free_mapは512ビットであり、チャンク上のページの割り当てを記録するために使用されます。 1 に設定されます。マップはページの割り当てタイプと割り当てられたページ数を記録するために使用されます。各ページは配列メンバーに対応し、最上位 2 桁はページの割り当てタイプを記録します。01 は大きい値、10 は大きい値です。 small を割り当てるときは、最初のチャンクから走査を開始し、チャンクに要件を満たすページがあるかどうかを確認します。現在のチャンクが適切でない場合は、適切なチャンクが存在しないまで次のチャンクを検索します。申請するときは、誰が十分なページを見つけたかはわかりませんが、途中のページの隙間を避けるために、割り当てられたページとできるだけ隙間を埋めるようにしてください。 (以降の割り当て時の検索数を減らすため) 1) 最初のチャンク グループ (0 ~ 63) からチェックを開始します。現在のグループに使用可能なページがある場合は、最初に現在のページ ビットの位置を確認します。最初と最後の空きページ、十分でない場合は、これらのページを 1 (割り当て済み) としてマークし、他のグループを検索します。ページが適切な場合は、直接使用します。ページが大きい場合は、検索を中断します。必要が大きい場合は、 、これは、利用可能ではあるが、最適ではないことを意味します。最終的に最適なチャンク (ページを最大限に活用できるチャンク) を比較するまで、他のチャンクの検索が続けられます。 2) 適切なページを見つけたら、対応するページを設定します。 3. 小さな割り当て:
対応する仕様のメモリが割り当てられているかどうかを最初に確認します。割り当てられていない場合、または割り当てが使い果たされている場合は、ページの割り当てプロセスは、対応するページ数のページに適用されます。ページに適用した後、固定サイズに従ってスロットに分割され、スロットが 1 つのリンクで接続されます。リストを作成し、リンクされたリストの先頭を AG (mm_heap)->free_slot に保存します
メモリ プールの解放の粒度はチャンクであり、efree 1 によって完了します。巨大なメモリ、大規模なメモリ、および小規模なメモリ タイプの解放は、チャンクの最初のページが占有されているため、オフセットを 0 にすることは不可能です。チャンクと相対的なので区別されます。 チャンクタイプと大タイプと小タイプが解放されると、占有されていたチャンクが解放され、AG リンクリストから削除されます。 2. 大メモリの解放: 計算されたオフセットが 0 でない場合。 、これはアドレスが大きいメモリまたは小さいメモリであることを意味し、オフセットに従ってページ番号を計算すると、チャンク->マップからページの割り当てタイプを取得でき、メモリを解放できます。指定されたタイプの Large は、チャンクを直接解放しませんが、解放後にチャンクが未割り当てであることが判明した場合は、チャンクを解放します。キャッシュ番号が特定の値に達すると、新しく追加されたチャンクはキャッシュされなくなり、システムは、キャッシュされたチャンクが見つかった場合に大量のメモリを占有しないようにします。 3. 小さいタイプのリリースの場合、解放されたスロットは、比較的単純なルール スロットの使用可能なリンク リストに直接挿入されます。
関連する推奨事項:
以上がPHPのメモリ管理について詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。