C ++アプリケーションのパフォーマンスの最適化(5) - オペレーティングシステムのメモリ管理

C ++アプリケーションのパフォーマンスの最適化(5) - オペレーティングシステムのメモリ管理

まず、オペレーティングシステムのメモリ管理の紹介

メモリにロードされたときに長い時間のために、コンピュータ・システムにおいて、メモリは希少で貴重な資源の一種で、アプリケーションにのみ実行することができます。メモリ空間が十分に大きい場合、早い段階で、同時に実行しているアプリケーションの数は、物理メモリよりもさらに、厳しく制限されるアプリケーションが特定の時刻に実行する必要がある場合、アプリケーションがメモリを実行しません。アプリケーションで問題を解決するために、仮想メモリの導入による近代的なオペレーティングシステム(Windows、Linux)のメモリ管理がメモリ不足にすることはできません。
本質的には、仮想メモリが完全にメモリにロードされていないときに実行するプログラムのコードとデータを作ることです。コードがメモリにロードされていない、またはデータがメモリにロードされていないアクセスする実行時動作中に、仮想メモリ・マネージャは、動的メモリにハードディスクから適切なコードやデータをロードします。そして、通常の状況下では、仮想メモリ・マネージャは、今後のロードされたコードまたはデータのための余地を作るために、ハードドライブに特定のコードまたはデータ・メモリの対応する第一の置換であろう。
コードの実行に関してメモリとハードドライブの間のデータ転送は非常に遅いので、前提の正しい動作を確保する上で仮想メモリ・マネージャはまた、実行されるデータコードまたはアクセスを回避するために、そのような置換アルゴリズムを最適化する必要性などの効率因子を考慮しなければならないためただメモリの交換、およびアクセスコードやデータのない長い時間がメモリに常駐してきました。仮想メモリ・マネージャは、プログラムに関与するディスクIOの数はできるだけ低く実行されるように、各プロセスは、合理的な数に維持し、メモリに常駐し、処理性能を動的パフォーマンスに基づいて調整データを符号化する必要がありますプログラムの業績を向上させます。

二、Windowsのメモリ管理

1、Windowsの仮想メモリ管理システム導入

Win32の仮想メモリマネージャは、プライベートと4ギガバイトの仮想アドレス空間のページ(32ビット)サイズは、線形プロセスに基づいて、各Win32プロセスを提供します。
自分のアドレス空間を心配していない各プロセスが独自のメモリ空間にアクセスすることしかできませんし、他のプロセスに属するアドレス空間にアクセスすることはできません、あるプライベートプロセスは、別のプロセスによって、親と子の関係を使用してデバッガなど(サンズプロセスの例外を、見ています)デバッグプロセスのアドレス空間にアクセスします。ランタイムDLLは、プロセスで使用されると、それはプロセス、DLLのグローバルデータ、および開くために、すべてのプロセスの仮想アドレス空間のアプリケーションからメモリDLL関数の呼び出しを通じての属する自分のアドレス空間に属しているが、仮想アドレス空間はなかったです。
仮想アドレス空間は、ページと呼ばれる単位に分割されたページに基づいて、ページのサイズは、基礎となるプロセッサによって決定され、x86プロセッサ・アーキテクチャ・ページ・サイズは4キロバイトです。Win32のページがプロセスの仮想メモリ・マネージャの最小単位であり、対応する物理メモリはまた、複数のページに分割されます。アプリケーションと仮想メモリアドレス空間のリリース、およびデータ転送やメモリやディスクの交換は、のページの最小単位です。
4ギガバイトは、アドレスのサイズの範囲は、プロセスが0x00000000のから0xFFFFFFFFのとすることができることを意味し、Win32プロセスが使用の低いゾーン2ギガバイトに残される、2ギガバイトシステム高いゾーンを使用するために予約しました。
Win32のは、16があるかもしれませんページングファイルと呼ばれる仮想メモリハードディスクファイルの実現を支援するために使用され、メモリのデータを格納するために使用されるページングファイルは、仮想メモリマネージャに置き換えられています。データページングファイルが再びプロセスにアクセスすると、仮想メモリマネージャは、ページングファイルからプロセスが適切にアクセスすることができ、そのメモリに置き換えられます。ユーザーは、効率性とパフォーマンス空間の理由のために、自分自身のページングファイルを設定することができ、プログラムコードは、それらがメモリ内のページに置き換えられているときに、(exeファイルとDLLを含む)は変更されず、ページングに書き込まれることはありませんファイルが、直接捨てられました。プログラムコードから直接exeファイルやdllファイルを見つけるために、再度、仮想メモリマネージャを必要なときにメモリに格納され、転送。さらに、同じプロセスに含まれる読み取り専用のデータおよびプログラムコードをexeファイルとDLLファイルの処理は、ページングファイルのオープンスペース内に格納されていません。
プロセスは、コードまたは特定のデータへのアクセスの一部を実行するが、ページフォールトエラーとして知られているメモリにない場合、コードまたはデータ、。多くの理由から、メモリの最も一般的なコードおよびデータ仮想メモリ・マネージャ置換のためのページフォールトは、仮想メモリ・マネージャは、コード内で実行されるか、メモリに転送される前にデータがアクセスされます。メモリの交換が大幅に仕事の開発を簡素化し、開発者に対して透過的です。しかし、ページングエラーがディスクIO、大幅にプログラムの全体的なパフォーマンスを低下させるページングエラーが多数含まれ、ページフォルトエラーと回避策の主な理由を知ることが必要です。

2、仮想メモリの使用

Win32の割り当てられたメモリを予約し、コミット2つの段階に分けています。したがって、三つの状態のプロセスのアドレス空間内の仮想ページ:無料の自由は、予約およびコミット提出脇に置きます。
無料とこのページがまだ割り当てられていない、新しいメモリ割り当て要求を満たすために使用することができます。
仮想アドレス空間は、地域(リージョン、ページの整数倍)別に設定されているから、メモリ空間は、新しいメモリ割り当て要求を満たすために描くことができないの予約を指しますが、必要なコードのために確保されたメモリのこのセクションのために後で使用します。予約は、物理メモリを割り当て、そのデータ構造(VAD、ディスクリプタの仮想アドレス)の状態に記載された方法を使用して仮想アドレス空間を追加していない場合には、記録用のメモリ空間のこのセクションは、予約されています。直接アクセスすることはできません本当の物理メモリの割り当て、したがって、予約されたスペースがないため、ご予約のアクションは比較的早く、予約ページへのアクセスは、メモリアクセス違反が発生します。
あなたが実際の物理メモリを取得したい場合は、提出、メモリが予約に提出しなければなりません。提出は、ページングファイルからスペースを開き、VADに適切なエントリを変更します。提出する際にも、すぐにバックアップ代替として、ディスクページングファイルスペースから物理メモリから領域を割り当てるが、オープンスペースませんでした。コードは、第1のメモリにデータを提出することを訪問すると、システムは、ページフォルトが運転スローされ、本当の物理メモリを見つけていません。今回は実際に物理メモリを割り当てますまで、仮想メモリマネージャがページフォルトを処理し、それはまた、予約を同時に提出することができます。そう時間のかかる操作よりも確保し、ディスクのページングファイルからスペースを開きます操作をコミットします。
仮想アドレスのWin32仮想メモリ管理デマンドページング戦略のではない実際の物理メモリの割り当てが少なく、実際のアクセスが必要です。パフォーマンス上の理由ではまず、デマンドページング戦略、セグメントは、全体的なパフォーマンスを完了し、改善に努めます。第二は、効率的な理由ではなく、実際にアクセスするためのスペースで、Win32のは、常に想定し、プロセスは、データのほとんどにアクセスしないと思いますので、不要なそのオープンスペースや収納スペースの利用率を向上させるためには、物理メモリにその代替、。
いくつかのプログラム場合があり、メモリのための大きな需要があるが、すぐにすべてのメモリを必要としない、実行パフォーマンスとストレージ効率の潜在的な需要を満たすために、物理ストレージからのワンタイムのオープンスペースが無駄です。需要が潜在的なだけであるため、割り当てられたメモリの最も可能性の高い大部分は、最終的には実際には使用しません。一回は、アプリケーション内のすべての物理メモリを割り当てた場合、それは非常にスペースの利用を削減します。
しかし、そうでない場合は、完全予約とメカニズムをコミットするが、すべての要求、異なる時点でのコードのために頻繁にメモリの要求を満たすためにメモリを割り当てる必要、との理由ポールの異なる時点のメモリで彼らの要求のギャップ空間(例えば、トラバーサルなど)へのアクセスが非常に良好な局所特徴の使用、仮想アドレスが連続していないので、RAMメモリは、異なる時点で得られた頻繁な要求コードを引き起こすことができる、他のコード・メモリによって要求がなくてもよい場合、その全体であろうこれにより、プログラムのパフォーマンスを減らし、ページフォルトの数を増やします。
予約・提出Win32プログラムは予約の着信MEM_RESERVEパラメータは、入ってくるMEM_COMMITパラメータを提出し、VirtualAllocの機能が完成される使用します。渡されたパラメータに応じて、仮想メモリ機能を使用してVirtualFree放出、および対応する機能VirtualAllocの、物理的メモリ領域に対応する仮想アドレスを解放することができるだけでなく、仮想アドレス領域の予約状況であってもよく、仮想アドレス領域に関連して一緒になるようにリリースは、仮想アドレス領域は、フリーの状態に戻ります。
スレッドスタックとヒーププロセスは、予約を使用して予約し、コミット二段階機構、Win32システム、スレッド・スタックを使用して実装され、二段階機構を犯し以下の通りであります:
あなただけの脇仮想アドレス領域を設定し、スレッドスタックを作成する場合、デフォルトは(のCreateThreadまたはリンクリンクオプションで変更できます)1Mで、最初の2つだけは、最初に提出されています。より提出ページのスレッドスタックの必要性は、仮想メモリマネージャは、動的に1Mの上限に達するまで、彼らのニーズを満たすために、後続のページでは、スレッドの仮想アドレス領域を提出する際に、ネストされた関数が呼び出されますので。予約領域のサイズ(デフォルト1M)の上限に達した場合、仮想メモリマネージャは、予約領域のサイズを増やしますが、スタックオーバーフロー例外をスローしません最後のページを提出する際、スタックオーバーフロー例外をする場合、スレッド・スタックスローされます利用可能なスペースがあり、プログラムが正常に実行することができます。プログラムは最後のページだけでなく、収納スペースのための継続的な必要性は、この時間が上限を超えた場合にスタック領域がなくなっ引き続き使用する場合は、直接プロセスを終了させます。
スレッドスタックオーバーフローが終了するプログラム全体を起こさないようにするためには、スタックのサイズの使用を制御するようにしてください。そのようなネスティング機能を低下させるように、再帰関数の使用を減らす、(ヒープが動的に拡大されるので、ヒープオープンスペースから大きなオブジェクトを格納し、スレッド・スタック・メモリ利用できることができる機能に大きなローカル変数を使用しないようにしよう面積は)私たちは、スレッドの生涯にわたって拡張することはできません、スレッドの作成時に固定されています。
スレッド・スタック・オーバーフローを防ぐためには、全プログラムが終了を引き起こし、それは最後のページを提出するときにスローされるスレッド・スタック・オーバーフロー、キャプチャオーバーフロー例外を生成することが例外処理スレッドの身体機能を追加し、それに応じて行動することができます。

3、プロセスフローの仮想メモリにアクセス

仮想メモリ領域が確保さ及び提出されたために、あなたは仮想メモリ領域内のデータに容易にアクセスしています。次のように特定のメモリアクセス処理があるプログラム時:
C ++アプリケーションのパフォーマンスの最適化(5) - オペレーティングシステムのメモリ管理
データが物理メモリ内に既に存在する場合は、仮想アドレスマネージャはデータのみの仮想メモリアドレスへの物理アドレスポイントにマップする必要がある、あなたは、物理メモリ内のデータにアクセスすることができます。この場合、それはより速く、ディスクIOを必要としません。
データメモリはちょうどの期間中に初めてアクセスしたときにそこには、実際の物理メモリの割り当て、または以前に訪問されたアクセスされているデータはありませんが、以来、仮想メモリマネージャの物理メモリに置き換えられましたので、提出、この時間がトリガされますページフォルト。仮想メモリマネージャがページフォルトを処理する、仮想メモリマネージャは、第1のデータがページングファイルではありませんが、exeファイルのデータは、ページングファイルのバックアップスペースにすでに存在するかどうかを検出する(バックアップスペースexeファイル、dllファイルのコードページと読み取り専用になりますバックアップディスクスペース内のデータへのアクセスがある場合は、dllファイル)は、仮想メモリ・マネージャは、物理メモリの右ページを見つけて、物理メモリにディスク交換にバックアップデータを格納します。
現在の物理メモリの空きページがあるかどうかを確認し、仮想メモリマネージャは、ページフレームデータベース(ページフレームデータベース)のデータ構造の名前を維持し、Windowsシステムの起動時に仮想メモリマネージャは、まず、このデータ構造は、オペレーティングシステム全体でこれは、初期化追跡し、各物理メモリページの状態を記録するために使用され、すべての必要な空きページ、一緒に接続する空きページ・リストで、この直接参照空きページ・リストがあれば、自由にページとして使用します。それ以外の場合は、ページングアルゴリズムに従って、最初のページを選択します。ページのみ転送ページ内の必要なデータを含有しながら、地域の特性を利用するために、ページング仮想メモリ・マネージャに転送されない場合、プログラム効率を向上させるために、いくつかのページで隣接するメモリに転送されます。メモリに転送し、最後以来のこのページは、このページを直接使用する変更されていない(コードページと読み取り専用ページをそのまま用いることもできる)場合は、メモリ・ページの選挙で、その後、このページのステータスを確認します。このページは変更されている場合は、このページのコンテンツに必要な適切なバックアップページにディスクページングファイルに書き込まれ、その後、空きページにこのページをマークしています。この時点で、データを保存するために使用されるフリーページがあった、アクセスされようとしています。仮想メモリマネージャは、これを使用するクリア直接フリーページがある場合は、このデータメモリとちょうどアプリケーションが最初のバックアップディスクページングファイルから該当ページを読んですることなく、アクセスされたかどうか、もう一度検出します。ない場合は、アクティブなページのページへのアイドル状態から、この空き領域、および、このページを読んで、適切なバックアップページ内のディスクのページングファイルが必要になります。
この場合、データが物理メモリページにすでにある、あなたは、仮想アドレスの物理アドレスにマッピングされたデータにアクセスすることができます。
ユーザー定義の配列、そしてちょうどそのページ内の境界線下のこの配列、およびこのページの次のページは無料または状態(非服従のために予約されたときに実際のデータアクセスは、状況は本当に、のような、より複雑になります物理メモリ)。プログラムダウン誤ってクロスボーダーのアクセスこの配列は、最初のページの障害に起因する場合。そして、仮想メモリマネージャは、ページングファイルに欠落しているページに対処する上でエラーを検出し、それは、いわゆるアクセス違反(アクセス違反)ではありません。仮想メモリアドレス、アクセス違反が指定されたアドレスが、実際の物理メモリとアドレスに対応する仮想メモリアドレスがないことを、提出されていないアクセスすることを意味し、アクセス違反が直接(クラッシュ)を終了するには、プロセス全体を引き起こします。
国境を越えたアクセスポインタ結果は、同じクロスボーダーのページはまだ後のアレイは、ページでの国境でないときに、異なる実行時の実際の状況に応じて変化し、この時点でのみ、誤ったアクセス(書き込み誤解や不当表示の原因となります、誤解唯一のコードが誤ってこのページのコードの実装)、他のデータに影響を与えます他の場所で書かれて実行されている影響し、全体のプロセスのクラッシュが発生することはありません。ページ、およびポインタ値がその隣接ページ後の範囲から外れたが、これは、隣接するページが、この時の状態の提出のためにもあれば間違ったアクセスがまだある場合、配列は本当に境界に存在するにもかかわらず、それがプロセスをクラッシュにつながることはありません。したがって、同じプログラム内のコードは、ポインタのアクセス違反エラーの配列があり、そして時々ない、実行しているときに時々クラッシュ。
隣接ページを強制的にフリーページのページは、各クロスボーダーアクセスは、その結果、アクセス違反が発生しながら、Microsoftは、ポインタが国境を越えたアクセスを検出するページヒープツールが、原理はメモリの各ページの境界に配置されている必須の割り当てで提供します開発段階でのクロスボーダーアクセスポインタエラーが公開されること、エラーが発生することなく公開バージョンに隠されたポインタのクロスボーダーアクセスされているので、プログラムのクラッシュは、エンドユーザーのアクセスまで発見されることはありません。

図4に示すように、仮想アドレスから物理アドレスへのマッピング

すでに後、物理メモリ内のデータへのアクセスを確保するには、最初にアドレスマッピングがデータにアクセスすることを、仮想アドレスを物理アドレスに変換する必要があります。
Win32のは、それぞれの4ギガバイトの仮想アドレス空間がプライベートプロセスので、各プロセスが自分のアドレスマッピングを実現するために、階層構造の独自のセットを維持し、2-アドレスマッピングテーブルの構造によって達成されます。4バイト単位で第一の層は、ページディレクトリテーブル(ページ・ディレクトリ)であり、実際のメモリページ(4キロバイト= 4096Byte)である1024に、各ページディレクトリエントリ(PDE、ページディレクトリエントリ)と呼ばれます;第二層は、テーブル、ページテーブル(ページテーブル)1024ページテーブルの合計です。ページテーブルに対応する各ページディレクトリページディレクトリエントリは、各ページテーブルもメモリページを占めました。4KBのページ・テーブル1024はまた、ページテーブルエントリ(PTE、ページ・テーブル・エントリ)と呼ばれる各4つのバイトに分割されます。各ページテーブルエントリは、物理メモリ内のページのフレームを指します。
C ++アプリケーションのパフォーマンスの最適化(5) - オペレーティングシステムのメモリ管理
Win32のは、3つの部分、最初の添字10ビット・ページ・ディレクトリ1024の位置決めのためのページディレクトリから成り、各仮想アドレスは32ビット整数値であり、仮想アドレス空間4ギガバイト(32ビット)サイズを提供します特に、特定の位置の値に応じて、ページ・テーブル・ページ・テーブルの第二の層を発見することができ、ページ・テーブル・インデックス、ページ・テーブル1024に位置決めするためのアイテムの後に10ビット、その値は、物理メモリ内のページかどうかを仮想アドレスによって表されるデータを含むページを見つけることができ、特定の物理ページバイト位置12の位置を特定するための12ビット・バイトのインデックスは単にページを配置することができる後バイトの任意の位置。
C ++アプリケーションのパフォーマンスの最適化(5) - オペレーティングシステムのメモリ管理
、ポインタ値0X2A8E317F、物理アドレスマッピング処理に仮想アドレスは以下のようにプログラム内のポインタ(仮想アドレス)にアクセスするとします。
C ++アプリケーションのパフォーマンスの最適化(5) - オペレーティングシステムのメモリ管理
ページディレクトリエントリは4バイトであるため0X2A8E317Fバイナリは、第一位置決め3つの分割され0,010,101,010,001,110 0,011,000,101,111,111、10ビット0010101010、位置決めにおけるページディレクトリのページディレクトリエントリの最初のものです左の2ビット0010101010は、0X2A8は添字、ページテーブルへのページディレクトリエントリポイントとして対応するページディレクトリエントリを見つける使用して、10 1010 1000(0X2A8)を得ました。ページテーブルの10ビット、すなわち後続位置決め0011100011ページテーブルエントリを使用して、左の2ビットは、0X38Bは添え字として、ページテーブルに対応するページ・テーブル・エントリを見つける使用して、0011100011 11 1000~1100(0X38B)です。ページテーブルエントリポイントは、実メモリを検索します。最後に、最後のデータ12ビットすなわち0001 0111 1111(0X17F)で、即ちこの点ポインタ、データ・ページ内に配置されました。
Win32のは、常にその物理メモリアドレスマッピングのデータ、および想定しています。このページに含まれるデータのためのマーカーを有するページ・テーブル・エントリは、物理メモリページに、取得したアドレスマッピングを行う場合は、このビットを検出するページテーブルエントリであり、エラーが不足しているページをスローされていない場合、次いでページングでこのページテーブルエントリは、このデータシートに記載されています場合は、ページングファイルにされ、このデータページ、このページテーブルエントリは、ページングファイルであり、そうでない場合は、アクセス違反のデータが含まれています開始位置ファイルは、その後、継続するために、ディスクの物理メモリアドレスマッピングプロセスから、このデータ・ページを転送します。各プロセスの仮想プライベートアドレス空間を実装し、各プロセスが異なるプロセスのために、独自のページディレクトリエントリとページテーブル構造を持ち、ページディレクトリページディレクトリエントリとページテーブルのページテーブルエントリが異なっているために、したがって、同じポインタ(仮想アドレス)を物理アドレスにマッピングされた異なるプロセスであることがポインタを渡す異なるプロセス間であり、異なる意味がありません。

状態レコードを使用して5、仮想メモリ空​​間

Win32虚拟内存管理器使用另一个数据结构来记录和维护每个进程的4GB虚拟地址空间的使用及状态信息,即虚拟地址描述符树(VAD,Virtua Address Discriptor)。每一个进程都有自己的VAD集合,VAD集合被组织成一个自平衡二叉树,以提高查找的效率。另外由于只有预留或提交的内存块才会有VAD,自由的内存块没有VAD(即不在VAD树结构中的虚拟地址块就是自由的)。
(1)当程序申请一块新内存时,虚拟内存管理器执行访问VAD,找到两个相邻VAD,只要小的VAD的上限与大的VAD的下限之间的差值满足所申请的内存块的大小需求,即可使用二者之间的虚拟内存。
(2)当第一访问提交的内存时,虚拟内存管理器总是假定要访问的数据所在数据页已经在物理内存中,并进行虚拟地址到物理地址映射。当找到相应的页目录项后发现页目录项并没有指向一个合法的页表,虚拟内存管理器就会查找进程的VAD树,找到包含该地址的VAD,并根据VAD中的信息,比如内存块大小、范围,以及在调页文件中的起始位置,随需生成相应的页表项。然后从刚才发生缺页错误的位置继续进行地址映射。因此,一个虚拟内存页被提交时,除了在调页文件中开辟一个备份页外,不会生成指向它的页表项的页表,也不会填充指向它的页表项,更不会开辟真正的物理内存页,而是直到第一次访问提交页时才会随需地从VAD中取得包含该页的整个区域的信息,生成相应页表,并填充相应页的页表项。
(3)当能够访问预留的内存时,虚拟地址管理器进行虚拟地址到物理地址的映射,找到相应的页目录项后发现页目录项并没有指向一个合法的页表,虚拟地址管理器就会查找进程的VAD树,找到包含该地址的VAD,此时发现此段内存块只是预留的,而没有提交,即没有对应物理内存,直接抛出访问违例,进程退出。
(4)当访问自由的内存时,虚拟地址内存管理器进行虚拟地址到物理地址的映射,找到相应的页目录项后发现页目录项并没有指向一个合法的页表,虚拟地址管理器就会查找进程的VAD树,发现并没有VAD包含此虚拟地址,发现此虚拟地址所在的虚拟内存页是自由状态,直接抛出访问违例,进程退出。

6、进程工作集

因为频繁的调页操作引起的磁盘IO会大大降低程序的运行效率,因此对每一个进程,虚拟内存管理器都会将一定量的内存页驻留在物理内存中,并跟踪其执行的性能指标,并动态调整驻留的内存页数量。Win32中驻留在物理内存中的内存页称为进程的工作集(working set),进程的工作集可以通过任务管理器查看,内存使用列即为工作集大小。
工作集是会动态变化的,进程初始时只有很少的代码页和数据页被调入物理内存。当执行到未被调入内存的代码或访问到尚未调入内存的数据时,相应代码页或数据页会被调入物理内存,工作集也会随之增加。但工作集不能无限增加,系统为每个进程设定了一个最小工作集和最大工作集,当工作集达到最大工作集大小,进程需要再次调入新页到物理内存时,虚拟内存管理器会架构原来工作集中某些内存页先置换出物理内存,然后再将需要调入的新页调入内存。
因为工作集的页驻留在物理内存中,对工作集页的访问不会涉及磁盘IO,因此速度非常快。如果访问的代码或数据不在工作集中,会引发额外的磁盘IO,从而降低程序的执行效率。极端情况下会出现所谓的颠簸或抖动(thrashing),即程序的大部分执行时间都花在调页操作上,而不是执行代码上。
虚拟内存管理器在调页时,不仅仅只是调入需要的页,同时还将其附近的页一起调入内存中,对于开发人员,如果要提高程序的运行效率需要考虑如下:
(1)对代码李硕,尽量编写紧凑代码,最理想情形是工作集不会达到最大阈值,在每次调入新页时,就不需要置换已经载入的内存页,因为根据locality特性,以前执行的代码和访问的数据在后面有很大可能会被再次执行好访问,因此程序执行时,缺页错误会大大降低,即减少磁盘IO。从进程任务管理器也可以查看一个进程从开始时到当前时刻共发生的缺页错误次数。即使不能达到理想情形,紧凑的代码往往意味着接下来执行的代码更大可能就在当前页或相邻页。根据时间locality特性,程序80%的时间花费在20%代码上,如果能将耗时的20%代码尽量紧凑且排在一起,会大大提高程序的整体性能。
(2)对数据来说,尽量将那些会一起访问的数据(如链表)放在一起,当访问数据时,数据在同一页或相邻页,只需要一次调页操作就可以完成。如果数据分散在分散在多个页(多个页不相邻),每次对数据的整体访问都会引发大量的缺页错误,从而降低性能。利用Win32提供的预留和提交两步机制,可以为一同访问的数据预留一大块空间,此时并没有分配实际存储空间,而是在后续执行过程中生成数据时格局需要提交内存,既不浪费存储空间(物理内存和磁盘的调页文件存储空间),又能利用locality特性。

三、Linux内存管理

1、Linux内存管理机制简介

Linux的内存管理主要分为两部分,一部分负责物理内存的申请与释放,物理内存的申请与释放的最小单位为页,在IA32中,页的大小为4KB;另一部分负责处理虚拟内存,虚拟内存的主要操作包括虚拟地址空间与物理地址空间的映射,物理内存页与磁盘页之间的置换等。

2、Linux进程的内存布局

一个32位Linux进程的地址空间为4GB,其中高位1GB,即0XC0000000--0XFFFFFFFF,为内核空间,低位3GB,即0X00000000--0XBFFFFFFF为用户地址空间。用户地址空间进一步被分为程序代码区、数据区(包括初始化数据区DATA和未初始化数据区BSS)、堆和栈。程序代码区占据最低端,往上是初始化数据区DATA和未初始化数据区BSS。代码区存放应用程序的机器代码,运行过程中代码不能修改,因此代码区内存为只读,且大小固定。数据区中存放应用程序的全局数据,静态数据和常量字符串,数据区大小也是固定的。
堆从未初始化数据区开始,向上端动态增长,增长过程中虚拟地址值变大;栈从高位地址开始,向下动态增长,虚拟地址值变小。
堆是应用程序在运行过程中动态申请的内存空间,如通过malloc/new动态生成对象或开辟内存空间时,最终会调用系统调用brk来动态调整数据区的大小。当申请的动态内存区域使用完毕,需要开发者明确使用相应的free/delete对申请的动态内存空间进行释放,free/delete最终也会使用brk系统调用调整数据区的大小。
栈是用来存放函数的传入参数、临时变量以及返回地址等数据,不需要通过malloc/new开辟空间,栈的增长与缩减是因为函数的调用与返回,不需要开发人员操作,没有内存泄漏的危险。
初始化数据区存放的是编译期就能够知道由程序设定初始值的全局变量及静态变量等,其初始值必须保存在最终生成的二进制文件中,并且在程序运行时会原封不动地将此区域映射到进程的初始化数据区。如果一个全局变量或静态变量在源代码中没有被赋初始值,在程序启动后,在第一次被赋值前,其初始值为0,本质上是有初始值的,其初始值为0。但当最终生成二进制文件时,未初始化数据区不会占据对应变量总大小的区域,而是只用一个值进行标识其未初始化数据区的总大小。如一个程序的代码指令有100KB,所有初始化数据总大小为100KB,所有未初始化数据总大小为150KB,则在最终生成的二进制文件中代码区有100KB,接着是100KB的初始化数据区,然后是4字节的大小空间,用于标记未初始化数据区大小,其值为150X1024,用于节省磁盘空间。但在进程虚拟地址空间中,对应未初始化数据区的大小必须是150KB,因为在程序运行时,程序必须真正能够访问到变量中的每一个,即当程序启动时,当检测到二进制文件中未初始化数据区的值为150X1024,则系统会开辟出150KB大小的区域作为进程的未初始化数据区并同时使用0对其进行初始化。

3、Linux物理内存管理

物理内存是用来存放代码指令与供代码指令操作的数据的最终场所,因此物理内存的管理是内存管理系统极其重要的任务。Linux使用页分配器(page allocator)来管理物理内存,页分配器负责分配和回收所有的物理内存页(物理内存的分配与回收的最小单位为4KB大小的页)。
页分配器的核心算法称为兄弟堆算法(buddy-heap algorithm),算法思想是每个物理内存区域都会有一个与之相邻的所谓兄弟区域,当两个区域被回收后,会被合并成为一个区域。如果被合并区域的相邻区域也被回收后,会被进一步合并为更大的区域。当有物理内存请求到来时,页分配器会首先检测是否有大小与之一致的区域。如果有,直接使用找到的匹配区域满足请求;如果没有,则找到更大的一个区域,并继续划分,直到分出的区域能够满足请求。为了配合兄弟堆算法,必须有链表来记录自由的物理内存区域,对于每个相同大小的自由区域,会有一个链表将其连接,每种大小的区域都会有一个链表对其进行管理。自由区域的大小都是2的幂。
当有一个8KB大小的内存请求到来,当前最小可供分配的区域为64KB,此时64KB会被划分为两个32KB,继而将低位的32KB继续划分为两个16KB大小的区域,再将最低位的16KB大小区域划分为两个8KB大小的区域,然后分配高位的8KB区域满足请求。

4、Linux虚拟内存管理

仮想メモリ・マネージャの主なタスクは、バックアップストレージとしてディスクファイルが存在する場合、領域が使用されているようなどのようにアプリケーション使用情報、(マップ)の仮想アドレス空間を維持することです。その場合は、ディスクの各領域に対応するものをエリアに、他の重要な特徴は、ページングでは、そのような特定のデータにアクセスするためのプログラムなど、物理メモリに転送されていない、仮想メモリマネージャは、測位データを担当し、物理メモリにそれを置き換えます。この時点では、物理メモリのページがなくて交換前の物理メモリ内の一部のページに、何の自由がない場合。
アプリケーションの仮想アドレス空間の使用状況情報のデータ構造を維持するために使用するvm_area_structです。各構造体は、数32にするvm_area_structは、リストのように接続されている場合よりもないプロセスの仮想アドレス空間が割り当てられた領域を説明するvm_area_struct; 32以上、全てをするvm_area_structとして編成されたとき検索のスピードアップに役立つ自己均衡バイナリツリー、。プログラムは、ポインタを介してデータにアクセスする場合、ポインタがアドレスポインタで示される任意の領域の代表は、ポインタにすなわち、不正アクセスが割り当てられていないと判定されたするvm_area_struct内に入ることが見出されていない場合、システムは、するvm_area_structツリーを問い合わせます。

5、仮想アドレスを物理アドレスにマッピングされています

ポインタは順番に、本質的に仮想アドレス値であるため、仮想アドレスの値が物理アドレス値に変換しなければならないため、プログラムへのポインタを介してデータにアクセスするときに真にアクセスデータは、それが参照するためにどの。
Linuxは、物理アドレスにマッピングされた3層戦略マップされた仮想アドレスを使用しています。LinuxとWindowsなどのため、同じIA32アーキテクチャのために、中間層が使用されていないWindowsの、そしてより多くの中層、と比較。

おすすめ

転載: blog.51cto.com/9291927/2406548