iOS記憶體管理

2021-09-24 07:35:23 字數 4482 閱讀 8134

引發反思 棧怎麼清除?會引發什麼狀況?怎麼使棧溢位? 堆空間怎麼清除?會引發什麼狀況?怎麼使堆溢位?

class a

a *a = [[a alloc] init];

複製**

記憶體是這麼分配的? a代表什麼?

內容分為虛擬記憶體+物理記憶體,正常初始化空間分配的都是虛擬記憶體,初始化例項才會占用物理內容 instrument的allocations工具可以監控 all heap & anoymous vm代表虛擬記憶體 dirty memory,resident size就是物理記憶體的大小

訪問記憶體其實都是訪問的邏輯位址,需要轉換後才能訪問物理記憶體, cpu根據邏輯位址通過界限暫存器判斷是否越界,越界位址錯誤,反之加上基址暫存器轉換成物理記憶體位址

ios使用的是低記憶體處理機制jetsam,基於優先順序佇列的機制。 當記憶體過低的時候,就會在佇列中進行廣播,希望大家盡量釋放記憶體,如果一段時間後,仍然記憶體不夠,就會開始kill程序,直到記憶體夠用。

2、堆:程式執行的過程中動態分配的儲存空間(建立的物件),手動申請的位元組空間需要呼叫free來釋放

3、bss段:沒有初始化的全域性變數和靜態變數,一旦初始化就會從bss段中收回掉,轉存到資料段中

4、資料段:存放已經初始化的全域性變數和靜態變數,以及常量資料,直到程式結束才會被立即收回

5、**段:程式編譯後的**內容,直到結束程式才會被收回

ios記憶體中的物件主要有兩類, 一類是值型別,比如int、float、struct等基本資料型別 一類是引用型別,也就是繼承nsobject類的所有oc物件 值型別會被放入棧中,遵循先進後出的原則 引用型別會放在堆中,當給物件分配記憶體空間時,對隨機在記憶體中開闢空間。

當兩個不同的物件各有乙個強引用指向對方,那麼迴圈引用就產生了,每個物件的引用計數都會+1,無法得到記憶體的釋放 weak是弱引用,計數器不會加一,並在引用物件被釋放的時候自動設定為nil

指標是不為nil,但是指向已經被釋放的記憶體的指標。 __unsafe_unretain或者assign的指標,物件釋放後會出現野指標。 一般情況下oc使用了weak指標,在物件銷毀時指標會置nil

問:__block什麼時候用?

答:在block裡面修改區域性變數的值都要用__block修飾

問:在block裡面, 對陣列執行新增操作, 這個陣列需要宣告成__block嗎?

答:不需要宣告成__block,因為testarr陣列的指標並沒有變(往陣列裡面新增物件,指標是沒變的,只是指標指向的記憶體裡面的內容變了)

問:在block裡面, 對nsinteger進行修改, 這個nsinteger是否需要宣告成__blcok ?

答:nsinteger的值發生改變,則要求新增__block修飾

複製**

預設情況下,block是存檔在棧中,可能被隨時**,通過copy操作可以使其在堆中保留乙份, 相當於一直強引用著, 因此如果block中用到self時, 需要將其弱化, 通過__weak或者__unsafe_unretained

自動釋放池是oc中記憶體自動**機制,它可以延遲加入autoreleasepool中變數release的時機,建立的變數會在超出其作用域的時候release

autorelease本質上就是延遲呼叫release

在沒有手加autorelease pool的情況下,autorelease物件是在當前的runloop迭代結束時釋放的,而它能夠釋放的原因是系統在每個runloop迭代中都加入了自動釋放池push和pop(每乙個執行緒都有乙個預設autoreleasepool)

int number = 4;

int *a = malloc(8);

a = &number;

free(a);

複製**

給指標a分配了8個位元組的位址,a又指向了number的位址,最後a釋放了。這時候釋放的是number的位址,而number是在棧區中,不能被手動釋放,這時候就出現了棧的記憶體洩露

預設情況下,乙個指標都會使用__strong屬性,表明這是乙個強引用。當所有強引用都去除時,物件才能被釋放

但有時候我們可能要禁止這種行為:一些集合類不應該增加其元素的引用,可能對導致物件無法釋放,可能會出現迴圈引用,導致無法釋放,這種情況下我們要使用弱引用__weak。

weak是弱引用,計數器不會加一,並在引用物件被釋放的時候自動設定為nil

在多執行緒環境下,每個執行緒擁有乙個棧和程式計數器,棧和程式計數器用來儲存執行緒執行歷史和執行緒執行的狀態,是執行緒私有資源

堆資源是統一程序內多執行緒共享的

一般來說我們可能會說「使用引用計數管理」,是的沒錯,但是引用計數是如何管理的呢?

2023年9月,蘋果推出了iphone5s,推出taggedpointer概念,為了節省記憶體和提高執行效率。 nsnumber,nsstring,nsdata等一些較小的資料taggedpointer將乙個物件的指標拆成兩部分,一部分直接儲存資料,另一部分作為特殊標記,表示這是乙個特別的指標,不指向任何乙個位址

正常object物件還是會使用引用計數來管理,那麼蘋果是這麼管理的呢?

為了管理記憶體蘋果內建了全域性的sidetables(雜湊表),儲存的是sidetable的結構體。

sidetable 結構體

struct sidetable

複製**

網上有個例子這裡:

100個學生(物件)住宿,大家都在乙個樓上(sidetables),有10個房間(sidetable),每個房間8個學生(obj); 關係類似於此

spinlock_t:自旋鎖,如果已經在訪問100號宿舍的某個學生,那麼在這時候是其他執行緒是無法訪問100號宿舍的其他學生,自旋鎖比較適用於鎖使用者保持鎖時間比較短的情況

refcountmap:物件具體的引用計數,沒錯就是他,所有strong等可以是引用計數+1的操作都會在這裡標記, 因為乙個sidetable可能有多個物件的計數器,sidetables[0x0000]和sidetables[0x0x000f] 可能都是同乙個sidetable,所以蘋果又提供了table.refcnts.find[0x0x000f]來找到真正的引用計數器 計數器的儲存結構:

) 看圖表示可以得知:真正引用計數器是從第三位開始,也就是4的位置。 如果這裡引用計數為0了,就會直接執行dealloc,檢視是否有weak引用會把weak_table_t中的弱引用置nil

weak_table_t:

struct weak_table_t ;

複製**

weak_entries:通過迴圈遍歷來找到對應的entry。

struct weak_entry_t ;

struct ; }}

複製**

為了管理所有的引用計數和weak指標,蘋果建立了乙個全域性的sidetables,它是乙個全域性的hash表,用於儲存指向某個物件的所有weak指標,key是所指向物件的位址,value是weak指標的位址陣列,裡面存放的都是sidetable結構體

weak是弱引用,計數器不會加一,並在引用物件被釋放的時候自動設定為nil

物件引用計數相關的操作是原子性的,如果多個執行緒同事操作乙個物件的引用計數會造成資料錯亂,同時在記憶體中的物件資料量大,不能讀整個hash加鎖,所以蘋果採用了分離鎖

nsobject *obj = [[nsobject alloc] init];

id __weak obj1 = obj;

複製**

初始化weak變數的時候,runtime會呼叫nsobject.mm中的obj_initweak函式,函式宣告如下:

id objc_initweak(id *object, id value);

複製**

而對於objc_initweak()方法的實現

id objc_initweak(id *location, id newobj) 

// 這裡傳遞了三個 bool 數值

// 使用 template 進行常量引數傳遞是為了優化效能

return storeweak

(location, (objc_object*)newobj);

}複製**

2、新增引用時,obj_initweak函式會呼叫obj_storeweak()函式更新指標指向,建立對應的弱引用表

obj_storeweak()函式宣告

id objc_storeweak(id *location, id value);

複製**

**具體看:

3、釋放時,呼叫cleardeallocating函式,首先根據物件位址獲取到所有的weak指標位址的陣列,然後遍歷這個陣列把其中的資料設為nil,最後把這個entry從weak表中刪除,最後清理物件的記錄

ios記憶體管理

引用計數 每個物件有乙個與之相關的整數,稱作 引用計數器 或者 保留計數器 當某段 需要訪問乙個物件時,該段 會將物件的保留計數器 1,表示需要訪問這個物件 當結束對該物件的訪問時,保留計數器 1,表示它不在訪問該物件 當保留計數器為0時,物件被銷毀,所佔記憶體被系統收回。當使用new retain...

iOS記憶體管理

前提 1 以下是針對cocoa物件,不包括core foundation 2 cocoa物件都是用引用計數來跟蹤物件的記憶體使用情況的。3 在子類裡面父類先初始化和後釋放的原則。自己想下為什麼 棧空間和堆空間的區別。我們說的記憶體管理都是基於堆空間的,因為函式內的棧空間是由編譯器自己控制的。關於co...

IOS 記憶體管理

範圍 任何繼承了nsobject的物件,對基本資料型別無效 原理 每個物件內部都儲存了乙個與之相關聯的整數,稱為引用計數器 當使用alloc new或者copy建立乙個物件時,物件的引用計數器被設定為1 給物件傳送一條retain訊息,可以使引用計數器值 1 給物件傳送一條release訊息,可以使...