参考ブログ:iOS メモリ管理学習パート 1 - 5 つのメモリ領域
3.1 OC の特徴 5 つのメモリ領域
1. 簡単な説明
プログラムを実行するには、最初のステップとしてプログラムをメモリにロードします。メモリには、スタック領域、ヒープ領域、静的領域、定数領域、コード セグメントの 5 つの主要な領域があります。
- スタック領域(スタック)は、関数のパラメータ値やローカル変数などを格納するために、コンパイラによって自動的に確保・解放されます。スタックはシステム データ構造であり、スレッド/プロセスに固有です。iPhone のスタック領域は 512K しかなく、その操作方法はデータ構造のスタックに似ています
長所: 高速かつ効率的 短所
: 制限があり、データの柔軟性が低い [先入れ後出し] - ヒープ領域(ヒープ)は、プログラマによって確保および解放されますが、プログラマが解放しないと、プログラム終了後にオペレーティングシステムによって再利用される可能性があります。リンクリストに似ている
長所:柔軟で便利、データの適応範囲が広い
短所:効率がある程度低下する - スタティック領域は、グローバル変数やスタティック変数の格納領域であり、プログラム終了後にシステムによって解放されます。
- 定数領域には定数文字列が格納され、プログラム終了後にシステムによって解放されます。
- コード領域には関数のバイナリコードが格納され、プログラム終了後にシステムによって解放されます。
2. ヒープ領域とスタック領域
int main(int argc, const char * argv[]) {
// 局部变量是保存在栈区的
// 栈区变量出了作用域之后,就会被销毁
NSInteger i = 10;
NSLog(@"%zd", i);
// 赋值语句右侧,使用 new\alloc\init 方法创建的对象是保存在堆区的
// xinge 变量中,记录的是堆区的地址
// 在 OC 中,有一个内存管理机制,叫做 `ARC`,可以自动管理 OC 代码创建对象的生命周期
// 因此,在开发 OC 程序的时候,程序员通常不需要考虑内存释放的工作
LJXPerson *xinge = [LJXPerson new];
NSLog(@"%@", xinge);
return 0;
}
2.1 スタック領域
スタック領域(stack [stæk]):コンパイラにより自動的に確保、解放される
2.1.1 スタック領域への保存(スタック領域の役割/格納内容)
- ローカル変数
- メソッドの実パラメータ (例: main 関数内、メソッド呼び出し、メソッドの実パラメータ)
2.1.2 スタック領域の特徴
- ストレージ容量は限られており、iPhone のスタック サイズはわずか 512k (デフォルト) で、非常に限られています。
- 連続性:スタック領域のアドレスは連続しています。
- アドレスは大きいものから小さいものへと割り当てられ、スタック領域のアドレスは割り当てられた順序に従って大きいものから小さいものへと配置されます。
- アクセス速度が速いです。
- システム管理 (スタック領域のメモリはシステムによって管理されます)
2.1.3 その他
プログラム内でメソッドを呼び出すと「スタックフレーム」が開かれます(このスタックフレームは連続した領域と考えることができます)
スタックフレームのアドレスは、前のローカル変数のアドレスと連続していません。パラメータアドレスはスタックフレーム
とメソッド内のローカル変数に記録されます。メソッドの実行後、スタック フレームが破棄されます (スタック ポップ)
<<<このようにして、各実行後にスタックがポップされてメモリが解放されます。これにより、常にスタック領域によって占有されていたメモリが確保されなくなります。特に大きい>>
課外用語-> 開発中に、各メソッドが非常に短く記述され、各メソッドで宣言される変数が非常に少ない場合、
これは間違いなくメモリを節約します。
要約する
メソッド呼び出し時のスタック領域の仕組み
- オープンスタックフレーム
- 実際のパラメータを保存する
- ローカル変数を保存する
- メソッドが完了すると、スタックがポップされ、スタック フレームが破棄され、スペースが解放されます。
2.2 ヒープ領域
ヒープ領域(heap [hiːp]):プログラマが確保・解放する領域で、プログラマが解放しないとメモリリークが発生します。
2.2.1 ヒープ領域に保存:
- 新しい \alloc\init メソッドを使用して作成されたオブジェクトはヒープ領域に保存されます
- 作成されたオブジェクトのすべてのメンバー変数はヒープ領域に格納されます
OC プログラムを開発する場合、プログラマは通常、メモリ解放作業を考慮する必要はありません。
ただし、OC コードの場合、C 言語の機能を使用して領域を確保する場合は、メモリの解放を考慮する必要があります。
- ヒープ領域のサイズは、システム メモリ/ディスク スワップ領域など、システムによって決定されます。
链表
ヒープ領域のメモリ割り当てを管理するためにシステムによって使用されます。- { プログラマはヒープ領域のメモリの割り当てと解放のみを担当する必要があります}
2.2.2 ヒープ領域の特徴
- すべてのプログラムで共有される
- ビッグデータを保存する
- プログラマ管理: ヒープ領域のメモリはプログラマによって管理される必要があります。
- Discontinuous: ヒープ領域のアドレスが不連続です
- スタック領域ほど速くない: ヒープ領域に作成されたオブジェクトのプロパティにアクセスしたい場合は、まずスタック領域のアドレスを見つける必要があるため、ヒープ領域のアクセス速度はスタック領域ほど速くありません。変数を使用して、そのアドレスを使用してヒープ領域内の特定の場所を見つけます。場所。この場所を見つけた後でのみ、このオブジェクトに格納されている属性に対応する値にアクセスできます。このアドレスを検索するプロセスにより、速度はスタック領域ほど速くありません。
3. グローバル変数、静的変数、定数
3.1 グローバル変数/スタティック変数/定数が保存されるメモリ領域
開発では変更を限られた範囲内に留める必要があります
3.1.1 グローバル変数/スタティック変数が保存されるメモリ領域
このセクションは、コンパイラ、オペレーティング システム、および特定のプラットフォームによって異なります。一部のコンパイラとプラットフォームには、メモリ レイアウトとセグメントの使用に関して独自の最適化戦略がある場合があります。したがって、詳細は異なる場合があり、詳細についてはコンパイラとプラットフォームのドキュメントを確認する必要があります。
Xcode13.3.1では、初期化されていないグローバル変数とスタティック変数はスタティック領域(.BSSセクション)に格納され、初期化されたグローバル変数とスタティック変数はデータ領域(.dataセクション)に格納されます。
データセグメントは通常、静的領域のサブ領域を指すことに注意してください。これらの用語は、システムやコンパイラごとに異なる方法で定義および使用される場合があります。場合によっては、データ セグメントと静的領域という用語も、同じメモリ領域を指すのに使用されます。
確認する:
検証コード:
// 设置两个全局变量,一个初始化,一个不初始化
int num1 = 1;
int num2;
int main(){
@autoreleasepool {
NSLog(@"num1 pointer = %p", &num1);
NSLog(@"num2 pointer = %p", &num2);
// 初始化num2
num2 = 2;
NSLog(@"init num2 pointer = %p", &num2);
// 设置两个静态变量,一个初始化,一个不初始化
static int sNum1 = 1;
static int sNum2;
NSLog(@"sNum1 pointer = %p", &sNum1);
NSLog(@"sNum2 pointer = %p", &sNum2);
sNum2 = 2;
NSLog(@"init sNum2 pointer = %p", &sNum2);
}
}
検証結果:
それは次のように知ることができます:
- スタティック変数およびグローバル変数は初期化されず、連続したアドレスでスタティック領域(.BSSセクション)に格納されます。
- スタティック変数とグローバル変数は初期化され、連続したアドレスでデータ領域 (.data セクション) に格納されます。
- 静的変数とグローバル変数の格納アドレスは初期化の前後で変化しません。これは、それらの格納領域が最初に決定され、その後変更されないことを示しています。
- スタティック領域(.BSSセグメント)とデータ領域(.dataセグメント)のアドレスは連続しており、その分割は厳密ではなく(動的に分割する必要があります)、1つの領域とみなすことができます。
3.1.2 定数を保存するメモリ領域
定数は定数領域に格納されます。
前の検証コードに定数を追加します。
// 设置两个全局变量,一个初始化,一个不初始化
int num1 = 1;
int num2;
int main(){
@autoreleasepool {
NSLog(@"num1 pointer = %p", &num1);
NSLog(@"num2 pointer = %p", &num2);
// 初始化num2
num2 = 2;
NSLog(@"init num2 pointer = %p", &num2);
// 设置两个静态变量,一个初始化,一个不初始化
static int sNum1 = 1;
static int sNum2;
NSLog(@"sNum1 pointer = %p", &sNum1);
NSLog(@"sNum2 pointer = %p", &sNum2);
sNum2 = 2;
NSLog(@"init sNum2 pointer = %p", &sNum2);
// 常量
const int cNum = 3;
NSLog(@"cNum pointer = %p", &cNum);
}
}
結果:
それは次のように知ることができます:
- 定数、スタティック変数、グローバル変数は同じ領域に格納されるのではなく、定数は定数領域に格納されます。
3.2 静的領域
スタティック変数とグローバル変数を格納する場合、最初に初期化するかどうかで.BSSセグメントに格納するか.dataセグメントに格納するかが決まり、一度決定すると変更されません。
3.3 定面積
初期化されているかどうかにかかわらず、定数を保存します。
4.タグ付きポインタ
以下は、メモリ管理を最適化するために 3 つのシステムによって作成された計画です。
1 つ目は、私たちが話している TaggedPointer です。TaggedPointer が使用される理由について詳しく説明されている公式ビデオ WWDC 2020 を視聴することをお勧めします。
- 小さなデータを保存するために特別に使用され、NSNumber、NSDate、NSTaggedPointerString 文字列などの上位ビットを通じてデータ型を直接決定します。
- スモールオブジェクトはヒープ領域には入りません 通常のデータを意識するのではなく、ポインタに値が直接格納されます ポインタにはアドレスが格納されるため、実際にはオブジェクトではなく、単なる通常の変数です 存在しませんヒープ内にあり、当然ながら、malloc と free は必要ありません。
- メモリ読み取り効率が 3 倍、作成速度が 106 倍高速化
図に示すように、これは TaggedPointer を使用するおおよその範囲です。