Linux中動態記憶體的分配與回收(heap, buddy system, stab)
夥伴系統演算法
在實際應用中,經常需要分配一組連續的頁框,而頻繁地申請和釋放不同大小的連續頁框,必然導致在已分配頁框的記憶體塊中分散了許多小塊的 空閒頁框這樣,即使這些頁框是空閒的,其他需要分配連續頁框的應用也很難得到滿足
為了避免出現這種情況,Linux核心中引入了夥伴系統演算法(buddy system)把所有的空閒頁框分組為11個 塊連結串列,每個塊連結串列分別包含大小為1,2,4,8,16,32,64,128,256,512和1024個連續頁框的頁框塊最大可以申請1024個連 續頁框,對應4MB大小的連續記憶體每個頁框塊的第一個頁框的實體地址是該塊大小的整數倍
假設要申請一個256個頁框的塊,先從256個頁框的連結串列中查詢空閒塊,如果沒有,就去512個 頁框的連結串列中找,找到了則將頁框塊分為2個256個 頁框的塊,一個分配給應用,另外一個移到256個頁框的連結串列中
頁框塊在釋放時,會主動將兩個連續的頁框塊合併為一個較大的頁框塊
1.2.slab分 配器
slab分配器源於 Solaris 2.4 的 分配演算法,工作於物理記憶體頁框分配器之上,管理特定大小物件的快取,進行快速而高效的記憶體分配
slab分配器為每種使用的核心物件建立單獨的緩衝區Linux 核心已經採用了夥伴系統管理實體記憶體頁框,因此 slab分配器直接工作於夥伴系 統之上每種緩衝區由多個 slab 組成,每個 slab就是一組連續的實體記憶體頁框,被劃分成了固定數目的物件
2.常用記憶體分配函式
2.1.__get_free_pages
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
__get_free_pages函式是最原始的記憶體分配方式,直接從夥伴系統中獲取原始頁框,返回值為第一個頁框的起始地址__get_free_pages在實現上只是封裝了alloc_pages函 數,從程式碼分析,alloc_pages函式會分配長度為1<<order的 連續頁框塊
2.2.kmem_cache_alloc
struct kmem_cache *kmem_cache_create(const char *name, size_t size,
size_t align, unsigned long flags,
void (*ctor)(void*, struct kmem_cache *, unsigned long),
void (*dtor)(void*, struct kmem_cache *, unsigned long))
void *kmem_cache_alloc(struct kmem_cache *c, gfp_t flags)
kmem_cache_create/ kmem_cache_alloc是基於slab分配器的一種記憶體分配方式,適用於反覆分配釋放同一大小記憶體塊的場合首先用kmem_cache_create建立一個快取記憶體區域,然後用kmem_cache_alloc從 該快取記憶體區域中獲取新的記憶體塊 kmem_cache_alloc一次能分配的最大記憶體由mm/slab.c檔案中的MAX_OBJ_ORDER巨集 定義,在預設的2.6.18核心版本中,該巨集定義為5, 於是一次最多能申請1<<5 * 4KB也就是128KB的 連續實體記憶體分析核心原始碼發現,kmem_cache_create函式的size引數大於128KB時會呼叫BUG()測試結果驗證了分析結果,用kmem_cache_create分 配超過128KB的記憶體時使核心崩潰
2.3.kmalloc
void *kmalloc(size_t size, gfp_t flags)
kmalloc是核心中最常用的一種記憶體分配方式,它通過呼叫kmem_cache_alloc函 數來實現kmalloc一次最多能申請的記憶體大小由include/Linux/Kmalloc_size.h的 內容來決定,在預設的2.6.18核心版本中,kmalloc一 次最多能申請大小為131702B也就是128KB字 節的連續實體記憶體測試結果表明,如果試圖用kmalloc函式分配大於128KB的記憶體,編譯不能通過
2.4.vmalloc
void *vmalloc(unsigned long size)
前面幾種記憶體分配方式都是物理連續的,能保證較低的平均訪問時間但是在某些場合中,對記憶體區的請求不是很頻繁,較高的記憶體訪問時間也 可以接受,這是就可以分配一段線性連續,物理不連續的地址,帶來的好處是一次可以分配較大塊的記憶體圖3-1表 示的是vmalloc分配的記憶體使用的地址範圍vmalloc對 一次能分配的記憶體大小沒有明確限制出於效能考慮,應謹慎使用vmalloc函式在測試過程中, 最大能一次分配1GB的空間
Linux核心部分記憶體分佈
2.5.dma_alloc_coherent
void *dma_alloc_coherent(struct device *dev, size_t size,
ma_addr_t *dma_handle, gfp_t gfp)
DMA是一種硬體機制,允許外圍裝置和主存之間直接傳輸IO資料,而不需要CPU的參與,使用DMA機制能大幅提高與裝置通訊的 吞吐量DMA操作中,涉及到CPU高速緩 存和對應的記憶體資料一致性的問題,必須保證兩者的資料一致,在x86_64體系結構中,硬體已經很 好的解決了這個問題, dma_alloc_coherent和__get_free_pages函式實現差別不大,前者實際是呼叫__alloc_pages函 數來分配記憶體,因此一次分配記憶體的大小限制和後者一樣__get_free_pages分配的內 存同樣可以用於DMA操作測試結果證明,dma_alloc_coherent函 數一次能分配的最大記憶體也為4M
2.6.ioremap
void * ioremap (unsigned long offset, unsigned long size)
ioremap是一種更直接的記憶體“分配”方式,使用時直接指定物理起始地址和需要分配記憶體的大小,然後將該段 實體地址對映到核心地址空間ioremap用到的實體地址空間都是事先確定的,和上面的幾種記憶體 分配方式並不太一樣,並不是分配一段新的實體記憶體ioremap多用於裝置驅動,可以讓CPU直接訪問外部裝置的IO空間ioremap能對映的記憶體由原有的實體記憶體空間決定,所以沒有進行測試
2.7.Boot Memory
如果要分配大量的連續實體記憶體,上述的分配函式都不能滿足,就只能用比較特殊的方式,在Linux內 核引導階段來預留部分記憶體
2.7.1.在核心引導時分配記憶體
void* alloc_bootmem(unsigned long size)
可以在Linux核心引導過程中繞過夥伴系統來分配大塊記憶體使用方法是在Linux核心引導時,呼叫mem_init函式之前 用alloc_bootmem函式申請指定大小的記憶體如果需要在其他地方呼叫這塊記憶體,可以將alloc_bootmem返回的記憶體首地址通過EXPORT_SYMBOL導 出,然後就可以使用這塊記憶體了這種記憶體分配方式的缺點是,申請記憶體的程式碼必須在連結到核心中的程式碼裡才能使用,因此必須重新編譯核心,而且記憶體管理系統 看不到這部分記憶體,需要使用者自行管理測試結果表明,重新編譯核心後重啟,能夠訪問引導時分配的記憶體塊
2.7.2.通過核心引導引數預留頂部記憶體
在Linux核心引導時,傳入引數“mem=size”保留頂部的記憶體區間比如系統有256MB內 存,引數“mem=248M”會預留頂部的8MB記憶體,進入系統後可以呼叫ioremap(0xF800000,0x800000)來申請這段記憶體
3.幾種分配函式的比較
分配原理 |
最大記憶體 |
其他 |
|
__get_free_pages |
直接對頁框進行操作 |
4MB |
適用於分配較大量的連續實體記憶體 |
kmem_cache_alloc |
基於slab機制實現 |
128KB |
適合需要頻繁申請釋放相同大小記憶體塊時使用 |
kmalloc |
基於kmem_cache_alloc實現 |
128KB |
最常見的分配方式,需要小於頁框大小的記憶體時可以使用 |
vmalloc |
建立非連續實體記憶體到虛擬地址的對映 |
物理不連續,適合需要大記憶體,但是對地址連續性沒有要求的場合 |
|
dma_alloc_coherent |
基於__alloc_pages實現 |
4MB |
適用於DMA操 作 |
ioremap |
實現已知實體地址到虛擬地址的對映 |
適用於實體地址已知的場合,如裝置驅動 |
|
alloc_bootmem |
在啟動kernel時,預留一段記憶體,核心看不見 |
小於實體記憶體大小,記憶體管理要求較高 |
相關推薦
Linux中動態記憶體的分配與回收(heap, buddy system, stab)
夥伴系統演算法 在實際應用中,經常需要分配一組連續的頁框,而頻繁地申請和釋放不同大小的連續頁框,必然導致在已分配頁框的記憶體塊中分散了許多小塊的 空閒頁框這樣,即使這些頁框是空閒的,其他需要分配連續頁框的應用也很難得到滿足 為了避免出現這種情況,Linux核心中引入了夥伴系統演算法(buddy
動態開點線段樹(多棵線段樹)的記憶體分配與回收
前言 線段樹,是一個很好用的能支援O(logn)區間操作的資料結構,隨著做一些稍微煩一點的題,有時候會發現有些情況要開一個數組的線段樹,更有甚者要樹套樹,而在很多情況下線段樹就不能把所有點都開滿了(否則會MLE記憶體超限),於是就出現了線段樹的動態開點寫法 基本思想 與普通的
我理解的堆疊(stack)、動態記憶體分配與堆(heap)
看到第4章,首次接觸到堆(heap)這個概念,不好理解,所以用vs2010反彙編跟蹤下程式: // use_new.cpp -- using the new operator #include <iostream> int
JVM記憶體分配與回收學習(2)
1、垃圾收集器什麼時候開始回收? (1)新生代有一個Eden區和兩個survivor區(From survivor 和To Survivor),每次使用Eden和其中一個Survivor(From Survivor),建立物件時,首先會將物件放入Eden區,如果放不下就會引發一次發生在新生代
動態記憶體分配與釋放(陳銳、葛麗萍 編著《跟我學資料結構》整理)
1.malloc函式 作用:在記憶體中分配一個長度為size 的連續儲存空間,返回一個指向分配空間的起始地址的指標。 如果分配失敗,則返回NULL。 原型: void *malloc(unsigned int size)
垃圾收集器與記憶體分配策略(六)——記憶體分配與回收策略
物件的記憶體分配,往大方向上講,就是在堆上分配(但也可能經過JIT編譯後被拆散為標量型別並間接地棧上分配),物件主要分配在新生代的Eden區上,如果啟動了本地執行緒分配緩衝,將按執行緒優先在TLAB上分配。少數情況下也可能會直接分配在老年代中,分配的規則並不是百分之百固定的,
JVM:GC-記憶體分配與回收策略
物件優先在Eden區分配 物件優先在eden區分配,當eden區沒有足夠空間分配記憶體時,就會發現minor gc. 程式碼例項: public class Main { static int _1M = 1024*1024; //vm 引數 // -ver
簡單的動態記憶體分配與釋放
掌握動態記憶體分配方法。 程式設計輸入學生人數n及某門課成績,計算並輸出其平均分的整數部分(請用malloc和free進行動態記憶體分配)。 input: 5 90 80 70 60 50 output: 70 #include <iostream> #include <mal
JVM六:記憶體分配與回收策略
對於物件的回收,前面以及講過具體的回收機制,下面我們來看看物件的分配策略! ①物件優先在Eden區域分配 大多數情況下,物件在新生代Eden區分配。當Eden區沒有足夠空間進行分配時,虛擬機器將發起一次Minor GC。 虛擬機器提供了-XX:PrintGCDetails這個收集日誌引數
JVM之記憶體分配與回收策略
JVM之記憶體分配與回收策略 來源 https://www.cnblogs.com/xiaoxi/p/6557473.html JVM分代垃圾回收策略的基礎概念 來源 https://www.cnblogs.com/xiaoxi/p/6602166.html 一、為
Linux中的記憶體分配和釋放之slab分配器分析(完)
我們在上篇文章分析cache_grow()函式的時候涉及兩個函式,我們沒有細說。一個就是kmem_getpages()和kmem_freepages()函式,這兩個函式有3個引數。kmem_cahce_t:主要是把申請到的物件加到這個快取記憶體內 flag
java虛擬機器-記憶體分配與回收策略
1.物件優先在Eden分配 大多數情況下,物件在新生代Eden區分配。當Eden區沒有足夠空間進行分配時,虛擬機器將發起一次Minor GC。 虛擬機器提供了-XX:+PrintGCDetails這個收集器日誌引數,告訴虛擬機器在傳送垃圾收集行為時列印記憶體回收日誌,並且在
Java虛擬機器(三):記憶體分配與回收策略
[GC[DefNew Desired survivor size 524288 bytes, new threshold 1 (max 1) - age 1: 748104 bytes, 748104 total : 5024K->730K(9216K), 0.0038710 sec
C語言之動態記憶體分配與釋放
一,堆記憶體 1,堆記憶體特點 堆記憶體可以存放任意型別的資料,但需要自己申請與釋放。 2,堆大小 堆大小,想像中的無窮大,但實際使用中,受限於實際記憶體的大小和記憶體是否有連續性。 二,堆記憶體的申請與釋放 1,malloc函式
記憶體分配與回收策略(五)
回收策略: Minor GC Major GC/Full GC Minor GC:發生在新生代的GC,發生非常頻繁,消耗時間短。 Major GC:發生在老年代GC,消耗時間一般為新生代GC的10倍,甚至更多(1000倍)。 Full GC:新生代+老年代 GC。
Linux C/C++ 記憶體分配與釋放 [摘抄整理]
no malloc no free no new no delete 寫了一個簡單類,執行的時候報了個錯 ,下決心好好看下記憶體相關知識 class ConstChar{ public: ConstChar(const char *data, int size)
記憶體分配與回收策略
物件的記憶體分配,往大方向上講,就是在堆上分配(但也可能經過JIT編譯後被拆散為標量型別並間接的在棧上分配),物件主要分配在新生代的Eden區上,如果啟動了本地執行緒分配緩衝,將按執行緒優先在TLAB上分配。少數情況下也可能會直接分配在老年代中,分配的規則
JVM 記憶體分配與回收策略
Java 中,物件的記憶體分配,大的方向講,就是在堆上分配。物件主要分配在新生代的eden區上。 記憶體分配規則: 大多數情況,記憶體在新生代eden區中分配,當eden區沒有足夠空間進行分配的時候,虛擬機器將發起一次minor GC 大物件直接
實體記憶體分配與回收(2)
1.物理頁面的分配 函式__get_free_pages用於物理頁塊的分配,其定義如下: unsigned long __get_free_pages(int gfp_mask, unsigned long order); 其中g
JVM_8_記憶體分配與回收策略
記憶體分配與回收策略 參考資料: 《Java虛擬機器垃圾回收(四) 總結:記憶體分配與回收策略 方法區垃圾回收 以及 JVM垃圾回收的調優方法》 在之前看"分代收集演算法"的時候,我們知道目前幾乎所有商業虛擬機器的垃圾收集器都採用分代收集演算法,對於Hotspot虛擬機