1. 程式人生 > >堆分配的記憶體空間是否連續

堆分配的記憶體空間是否連續

版權宣告:本文為博主原創文章,未經博主允許不得轉載,轉載請標明原文連結 。 https://blog.csdn.net/jin13277480598/article/details/54409543

附:如果想更加了解堆和棧的區別,以及理解堆是什麼,棧是什麼等問題,可以先看博文

很多人都有這樣的疑問:堆分配的記憶體空間到底是連續的還是不連續的,如malloc分配的記憶體空間是連續的嗎?

問題的引出

堆包含一個連結串列來維護已用和空閒的記憶體塊。在堆上新分配(用 new 或者 malloc)記憶體是從空閒的記憶體塊中找到一些滿足要求的合適塊。所以可能讓人覺得只要有很多不連續的零散的小區域,只要總數達到申請的記憶體塊,就可以分配。

   但事實上是不行的,這又讓人覺得是不是零散的記憶體塊不能連線成一個大的空間,而必須要一整塊連續的記憶體空間才能申請成功呢。

答案:

  1. 申請和釋放許多小的塊可能會產生如下狀態:在已用塊之間存在很多小的空閒塊。進而申請大塊記憶體失敗,雖然空閒塊的總和足夠,但是空閒的小塊是零散的,不能滿足申請的大小,。這叫做“堆碎片”。
  2. 當旁邊有空閒塊的已用塊被釋放時,新的空閒塊會與相連的空閒塊合併成一個大的空閒塊,這樣就可以有效的減少"堆碎片"的產生。
  3. 堆分配的空間在邏輯地址上是連續的,但在實體地址上是不連續的(因為採用了頁式記憶體管理,windows下有段機制、分頁機制),如果邏輯地址空間上已經沒有一段連續且足夠大的空間,則分配記憶體失敗。

簡要介紹Windows分段分頁知識

     windows執行的幾種模式,包括真實模式、保護模式以及虛擬8086模式。一般作業系統執行在保護模式下,程式可以使用4G大小的虛擬空間。

邏輯地址(Logical Address)

         是指由程式產生的與段相關的偏移地址部分。例如,你在進行C語言指標程式設計中,可以讀取指標變數本身值(&操作),實際上這個值就是邏輯地址,它是相對於你當前程序資料段的地址,不和絕對實體地址相干

       只有在Intel真實模式下,邏輯地址才和實體地址相等(因為真實模式沒有分段或分頁機制,cpu不進行自動地址換);邏輯也就是在Intel 保護模式下程式執行程式碼段限長內的偏移地址(假定程式碼段、資料段如果完全一樣)。

        應用程式設計師僅需與邏輯地址打交道,而分段和分頁機制對您來說是完全透明的,僅由系統程式設計人員涉及。應用程式設計師雖然自己可以直接操作記憶體,那也只能在作業系統給你分配的記憶體段操作。

線性地址(Linear Address) 

      是邏輯地址到實體地址變換之間的中間層。程式程式碼會產生邏輯地址,或者說是段中偏移地址,加上相應段的基地址就生成了一個線性地址。如果啟用了分頁機制,那麼線性地址可以再經變換以產生一個實體地址。若沒有啟用分頁機制,那麼線性地址直接就是實體地址。Intel 80386的線性地址空間容量為4G(2的32次方即32根地址匯流排定址)。

實體地址(Physical Address) 

      是指出現在CPU外部地址總線上的定址實體記憶體的地址訊號,是地址變換的最終結果地址。如果啟用了分頁機制,那麼線性地址會使用頁目錄和頁表中的項變換成實體地址

       分頁機制對映之後得到的地址。也就是程式程式碼、資料實際在記憶體中被載入的地址

  如果沒有啟用分頁機制,那麼線性地址就直接成為實體地址了。

總結:Windows分段和分頁機制存在的目的就是為了讓邏輯地址通過分段和分頁對映到實體記憶體中。

Linux 下面的分析

malloc分配的記憶體空間是連續的嗎?

1、linux核心管理記憶體空間的分配,所有程式對記憶體空間的申請和其他操作,最終都會交給核心來管理。

2、linux實現的是“虛擬記憶體系統”,對使用者而言,所有記憶體都是虛擬的,也就是說程式並不是直接執行在實體記憶體上,而是執行在虛擬記憶體上,然後由虛擬記憶體轉換到實體記憶體。

3、linux將所有的記憶體都以頁為單位進行劃分,通常每一頁是4KB;

4、在對虛擬記憶體地址到實體記憶體地址進行轉換時,核心會對地址的正確性進行檢查,如果地址是合法的,核心就會 提供對應的實體記憶體分頁;如果是申請記憶體空間,核心就會檢查空餘的實體記憶體分頁,並加以分配,如果實體記憶體空間不足,核心會拒絕此次申請;

 5、使用malloc分配的記憶體空間在虛擬地址空間上是連續的,但是轉換到實體記憶體空間上有可能是不連續的,因為有 可能相鄰的兩個位元組是在不同的物理分頁上;