欧美性猛交xxxx免费看_牛牛在线视频国产免费_天堂草原电视剧在线观看免费_国产粉嫩高清在线观看_国产欧美日本亚洲精品一5区

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

淺析物理內存與虛擬內存的關系及其管理機制

Linux愛好者 ? 來源:程序員不是碼農 ? 作者:程序員不是碼農 ? 2021-04-12 09:55 ? 次閱讀

本文主要介紹內存管理機制:物理內存與虛擬內存的關系,Linux內存管理機制,Python內存管理機制,Nginx內存管理機制,環(huán)形緩沖區(qū)機制,以及TC-malloc內存分配器的Andriod管理機制的簡單介紹。

一。 物理內存與虛擬內存

眾所周知,程序需要加載到物理內存才能運行,多核時代會出現(xiàn)多個進程同時操作同一物理地址的情況,進而造成混亂和程序崩潰。計算機當中很多問題的解決都是通過引入中間層,為解決物理內存使用問題,虛擬內存作為中間層進入了操作系統(tǒng),從此,程序不在直接操作物理內存,只能看到虛擬內存,通過虛擬內存,非常優(yōu)雅的將進程環(huán)境隔離開來,每個進程都擁有自己獨立的虛擬地址空間,且所有進程地址空間范圍完全一致,也給編程帶來了很大的便利,同時也提高了物理內存的使用率,可同時運行更多的進程。

物理內存和虛擬內存之間的關系

虛擬內存以頁為單位進行劃分,每個頁對應物理內存上的頁框(通常大小為4KB),內存管理單元(MMU)負責將虛擬地址轉換為物理地址,MMU中有一張頁表來存儲這些映射關系。

并非虛擬內存中所有的頁都會分配對應的物理內存,為充分利用物理內存,保證盡可能多的進程運行在操作系統(tǒng)上,因此需要提高物理內存利用率,對于很少用到的虛擬內存頁不分配對應的物理內存,只有用到的頁分配物理內存。雖然從程序角度來看,虛擬內存為連續(xù)地址空間,但其實,它被分隔成多個物理內存碎片,甚至還有部分數(shù)據(jù)并不在內存中,而是在磁盤上。

當訪問虛擬內存時,通過MMU尋找與之對應的物理內存,如果沒有找到,操作系統(tǒng)會觸發(fā)缺頁中斷,從磁盤中取得所缺的頁并將其換入物理內存,并在頁表中建立虛擬頁與物理頁的映射關系。

如果物理內存滿了,操作系統(tǒng)會根據(jù)某種頁面置換算法(比如LRU算法),將物理內存對應的頁換出到磁盤,如果被換出的物理內存被修改過,則必須將其寫回磁盤以更新對應的副本。

當進程創(chuàng)建時,內核為進程分配4G虛擬內存,此時,僅僅只是建立一個映射關系,程序的數(shù)據(jù)和代碼都還在磁盤中,只有當運行時才換回物理內存。并且,通過malloc來分配動態(tài)內存時,也只分配了虛擬內存,并不會直接給物理內存,因此,理論上來說malloc可分配的內存大小應該是無限制的(實際當然會有很多算法進行限制)。

多進程使用同一物理內存圖如下:

34cbac64-9b2f-11eb-8b86-12bb97331649.png

物理內存與虛擬內存關系

二。 Linux內存管理機制進程地址空間

進程地址空間分為內核空間(3G到4G)和用戶空間(0到3G),如下圖。

34e647c2-9b2f-11eb-8b86-12bb97331649.png

進程內存地址空間

內核通過brk和mmap來分配(虛擬)內存,malloc/free底層實現(xiàn)即為brk, mmap和unmmap

當malloc內存小于128k時采用brk,其通過將數(shù)據(jù)段(.data)的地址指針_edata往高地址推來分配內存,brk分配的內存需要高地址內存全部釋放后才會釋放,當最高地址空間空閑內存大于128K時,執(zhí)行內存緊縮操作。

當malloc內存大于128K時采用mmap,其在堆棧中間的文件映射區(qū)域(Memory Mapping Segment)找空閑虛擬內存,mmap分配的內存可單獨釋放。

每個進程都對應一個mm_struct結構體,即唯一的進程地址空間

// include/linux/mm.h

struct vm_area_struct {

struct mm_struct * vm_mm;

};

// include/linux/sched.h

struct mm_struct {

struct vm_area_struct *mmap; // vma鏈表結構

struct rb_root mm_rb; // 紅黑樹指針

struct vm_area_struct *mmap_cache; // 指向最近找到的虛擬區(qū)間

atomic_t mm_users; // 正在使用該地址的進程數(shù)

atomic_t mm_count; // 引用計數(shù),為0時銷毀

struct list_head mmlist; // 所有mm_struct結構體都通過mmlist連接在一個雙向鏈表中

};

linux內核用struct page結構體表示物理頁:

// include/linux/mm.h

struct page {

unsigned long flags; // 頁標識符

atomic_t count; // 頁引用計數(shù)

struct list_head list; // 頁鏈表

struct address_space *mapping; // 所屬的inode

unsigned long index; // mapping中的偏移

struct list_head lru; // LRU最近最久未使用, struct slab結構指針鏈表頭變量

void *virtual; // 頁虛擬地址

}

內存碎片與外存碎片

內存碎片

產生原因:分配的內存空間大于請求所需的內存空間,造成內存碎片

解決辦法:伙伴算法,主要包括內存分配和釋放兩步:

內存分配:需滿足兩個條件,1) 大于請求所需內存;2)為最小內存塊(如64K為一頁)的倍數(shù)。比如,最小內存塊為64K,若分配100K內存,則應分配64*2=128K內存大小。

內存釋放:包含兩步,1)釋放內存;2)檢查是否可與相鄰塊合并,直到沒有可合并內存塊。

接下來通過一張圖來詳細說明伙伴算法原理(From wiki),如下:

3512638e-9b2f-11eb-8b86-12bb97331649.png

伙伴算法圖解

Step步驟詳解(注意最左側Step為步驟,ABCD申請者對應不同的顏色):

初始化內存,最小內存塊為64K,分配1024KB(只截取部分進行說明)

A申請34K內存,因此需64K內存塊,步驟2.1 2.2 2.3 2.4都為對半操作,步驟2.5找到滿足條件的塊(64K),分配給A

B申請66K內存,因此需要128K內存塊,有現(xiàn)成的直接分配

C申請35K內存,需64K內存塊,直接分配

D申請67K內存,需128K內存塊,步驟5.1對半操作,步驟5.2分配

釋放B內存塊,沒有相鄰內存可合并

釋放D內存塊,步驟7.1釋放內存,步驟7.2 與相鄰塊進行內存合并

A釋放內存,不許合并內存

C釋放內存,步驟9.1釋放內存,步驟9.2-9.5進行合并,整塊內存恢復如初

以上為伙伴算法原理,Linux關鍵代碼在mm/page_alloc.c中,有興趣讀者可在內核源碼中閱讀細節(jié),如下:

//mm/page_alloc.c

// 塊分配, removing an element from the buddy allocator

// 再zone中找到一個空閑塊,order(0:單頁,1:雙頁,2:4頁 2 ^ order)

static struct page * __rmqueue(struct zone *zone, unsigned int order)

{

}

// 塊釋放,處理合并邏輯

static int

free_pages_bulk(struct zone *zone, int count, struct list_head *list, unsigned int order) {

}

這里簡單介紹云風實現(xiàn)的伙伴算法,實現(xiàn)思路:用數(shù)組實現(xiàn)完全二叉樹來管理內存,樹節(jié)點標記使用狀態(tài),在分配和釋放中通過節(jié)點的狀態(tài)來進行內存塊的分離與合并,如下:

// 數(shù)組實現(xiàn)二叉樹

struct buddy {

int level; // 二叉樹深度

uint8_tree[1]; // 記錄二叉樹用來存儲內存塊(節(jié)點)使用情況,柔性數(shù)組,不占內存

};

// 分配大小為s的內存

int

buddy_alloc(struct buddy * self, int s) {

// 分配大小s的內存,返回分配內存偏移量地址(首地址)

int size;

if (s == 0) {

size = 1;

} else {

// 獲取大于s的最小2次冪

size = (int)next_pow_of_2(s);

}

int length = 1 《《 self-》level;

if(size 》 length)

return -1;

int index = 0;

int level = 0;

while (index 》= 0) {

//具體分配細節(jié)。..

}

return -1;

}

// 釋放內存并嘗試合并

void

buddy_free(struct buddy * self, int offset) {

// 釋放偏移量offset開始的內存塊

int left = 0;

int length = 1 《《 self-》level;

int index;

for (;;) {

switch(self-》tree[index]) {

case NODE_USED:

_combine(self, index); // 嘗試合并

return;

case NODE_UNUSED:

return;

default:

// 。..

}

}

}

外存碎片

產生原因:未被分配的內存,出現(xiàn)大量零碎不連續(xù)小內存,無法滿足較大內存申請,造成外部碎片

解決辦法:采用slab分配器,處理小內存分配問題,slab分配器分配內存以字節(jié)為單位,基于伙伴系統(tǒng)分配的大內存進一步細分成小內存分配

slab分三種:slabs_full(完全分配的slab),slabs_partial(部分分配的slab),slabs_empty(空slab),一個slab分配滿了之后就從slabs_partial刪除,同時插入到slab_fulls中。

slab兩個作用:1)小對象分配,不必每個小對象分配一個頁,節(jié)省空間;2)內核中一些小對象創(chuàng)建析構頻繁,slab對小對象緩存,可重復利用一些相同對象,減少內存分配次數(shù)。(應用于內核對象的緩存)。

slab分配器基于對象(內核中數(shù)據(jù)結構)進行管理,相同類型對象歸為一類,每當申請這樣一個對象,slab分配器就從一個slab列表中分配一個這樣大小的單元,當釋放時,將其重新保存到原列表中,而不是直接返還給伙伴系統(tǒng),避免內存碎片。slab分配對象時,會使用最近釋放的對象的內存塊,因此其駐留在cpu高速緩存中的概率會大大提高

3523d970-9b2f-11eb-8b86-12bb97331649.png

Slab分配器

三。 Python內存管理機制內存管理層次結構

35634380-9b2f-11eb-8b86-12bb97331649.png

Python內存層次結構

Layer 0:操作系統(tǒng)提供的內存管理接口,比如malloc,free,python不能干涉這一層

Layer 1:封裝malloc,free等接口PyMem_API,提供統(tǒng)一的raw memory管理接口,為了可移植性。

Layer 2:構建了更高抽象層次的內存管理策略(GC藏身之處)

Layer 3:對象緩沖池

// 第1層 PyMem_Malloc通過一個宏PyMem_MALLOC實現(xiàn)

// pymem.h

PyAPI_FUNC(void *) PyMem_Malloc(size_t);

PyAPI_FUNC(void *) PyMem_Realloc(size_t);

PyAPI_FUNC(void *) PyMem_Free(size_t);

#define PyMem_MALLOC(n) ((size_t)(n) 》 (size_t)PY_SSIZE_T_MAX ? NULL

: malloc(((n) != 0) ? (n) : 1))

#define PyMem_MALLOC(n) ((size_t)(n) 》 (size_t)PY_SSIZE_T_MAX ? NULL

: realloc(((n) != 0) ? (n) : 1))

#define PyMem_FREE free

// Type-oriented memory interface 指定類型

#define PyMem_New(type, n)

( ((size_t)(n) 》 PY_SSIZE_T_MAX / sizeof(type)) ? NULL :

( (type*)PyMem_Malloc((n) * sizeof(type))) ) )

#define PyMem_NEW(type, n)

( ((size_t)(n) 》 PY_SSIZE_T_MAX / sizeof(type)) ? NULL :

( (type*)PyMem_MALLOC((n) * sizeof(type))) ) )

小塊空間的內存池

Python內存池可視為一個層次結構,自下而上分為四層:block,pool,arena和內存池(概念),其中bock, pool, arena在python中都能找到實體,而內存池是由所有這些組織起來的一個概念。

Python針對小對象(小于256字節(jié))的內存分配采用內存池來進行管理,大對象直接使用標準C的內存分配器malloc。

對小對象內存的分配器Python進行了3個等級的抽象,從上至下依次為:Arena,Pool和Block。即,Pool由Block組成,Arena由Pool組成。

Block

block內存大小值被稱為size class, 大小為:[8, 16, 24, 32, 40, 48 。.. 256],(8*n),內存管理器的最小單元,一個Block存儲一個Python對象。

// obmalloc.c

// 8字節(jié)對齊

#define ALIGNMENT 8

#define ALIGNMENT_SHIFT 3

#define ALIGNMENT_MASK (ALIGNMENT - 1)

// block大小上限為256,超過256KB,則交由第一層的內存管理機制

#define SMALL_REQUEST_THRESHOLD 256

#define NB_SMALL_SIZZE_CLASSES (SMALL_REQUEST_THREASHOLD / ALIGNMENT)

// size class index 轉換到 size class

#define INDEX2SIZE(I) (((unit) (I)) + 1) 《《 ALIGMENT_SHIFT)

// sizes class 轉換到size class index

size = (uint )(nbytes - 1) 》》 ALIGMENT_SHIFT;

小于256KB的小塊內存分配如下圖。

3671ac80-9b2f-11eb-8b86-12bb97331649.png

Block分配策略

如果申請內存大小為28字節(jié),則PyObject_Malloc從內存池中分配32字節(jié)的block,size class index為3的pool(參考上圖)。

Pool

Pool為一個雙向鏈表結構,一系列Block組成一個Pool,一個Pool中所有Block大小一樣;一個Pool大小通常為4K(一個虛擬/系統(tǒng)內存頁的大小)。

一個小對象被銷毀后,其內存不會馬上歸還系統(tǒng),而是在Pool中被管理著,用于分配給后面申請的內存對象。Pool的三種狀態(tài)

used狀態(tài):Pool中至少有一個Block已被使用,且至少還有一個Block未被使用,存在usedpools數(shù)組中。

full狀態(tài):Pool中所有的block都已經被使用,這種狀態(tài)的Pool在Arena中,但不再Arena的freepools鏈表中

empty狀態(tài):Pool中所有的Block都未被使用,處于這個狀態(tài)的Pool的集合通過其pool_head中的nextpool構成一個鏈表,表頭為arena_object中的freepools

// obmalloc.c

#define SYSTEM_PAGE_SIZE (4 * 1024)

#define SYSTEM_PAGE_SIZE_MASK (SYSTEM_PAGE_SIZE - 1)

// 一個pool大小

#define POOL_SIZE SYSTEM_PAGE_SIZE

#define POOL_SIZE_MASK SYSTEM_PAGE_SIZE_MASK

/*pool for small blocks*/

struct pool_header {

union {

block *_padding;

uint count; }ref; // 分配的block數(shù)量

block *freeblock; // 指向pool中可用block的單向鏈表

struct pool_header *nextpool; // 指向下一個

struct pool_header *prevpool; // 指向上一個

uint arenaindex;

// 記錄pool保存的block的大小,一個pool中所有block都是szidx大小

// 和size class index聯(lián)系在一起

uint szidx;

uint nextoffset;

uint maxnextoffset;

};

typedef struct pool_header *poolp;

擁有相同block大小的pool通過雙向鏈表連接起來,python使用一個數(shù)組usedpools來管理使用中的pool

36ca69a6-9b2f-11eb-8b86-12bb97331649.png

Userpools結構

以下為Python內存分配部分代碼:

// obmalloc.c

typedef uchar block;

void *

PyObject_Malloc(sizes_t nbytes)

{

block *bp; // 指向從pool中取出第一塊block的指針

poolp pool; // 指向一塊4kb內存

poolp next;

uint size;

// 小于SMALL_REQUEST_THRESHOLD 使用Python的小塊內存的內存池,否則走malloc

if ((nbytes - 1) 《 SMALL_REQUEST_THRESHOLD) {

// 根據(jù)申請內存的大小獲得對應的獲得size class index, 從usedpools中取pool

size = (uint)(nbytes - 1) 》》 ALIGNMENT_SHIFT;

pool = usedpools[size + size];

// 如果usedpools中有可用pool, 使用這個pool來分配block$

if (pool != pool-》nextpool) {

。..

}

}

}

Arena

Arena是Python直接從操作系統(tǒng)分配和申請內存的單位,一個Arena為256KB,每個Arena包含64個Pool,Arena管理的內存是離散的,Pool管理的內存是連續(xù)的。同Pool,Arena也是一個雙向鏈表結構。

3744b2d8-9b2f-11eb-8b86-12bb97331649.png

Arena結構

Python在分配Pool的時候優(yōu)先選擇可用Pool數(shù)量少的Arena進行內存分配,這樣做的目的是為了讓Pool更為集中,避免Arena占用大量空閑內存空間,因為Python只有在Arena中所有的Pool全為空時才會釋放Arena中的內存。

Python中會同時存在多個Arena,由Arenas數(shù)組統(tǒng)一管理。

// obmalloc.c

#define ARENA_SIZE (256 《《 10) // 256kb

// arena包含arena_object及其管理的pool集合,就如同pool和pool_header一樣

struct arena_object {

uintptr_t address; // arena地址

block* pool_address; // 下一個pool地址

uint nfreepools;

uint ntotalpools;

struct pool_header* freepools; // 可用pool通過單鏈表連接

struct arena_object* nextarena;

struct arena_object* prearena;

};

// arenas管理著arena_object的集合

static struct arena_object* arenas = NULL;

// 未使用的arena_object鏈表

static struct arena_object * unused_arena_objects = NULL;

// 可用的arena_object鏈表

static struct arena_object * usable_arenas = NULL;

static struct arena_object * nwe_arena(void)

{

struct arena_object * arenaobj;

uint excess;

// 判斷是否需要擴充“未使用的”arena_object列表

if (unused_arena_objects == NULL) {

// 確定本次需要申請的arena_object的個數(shù),并申請內存

numarenas = maxarenas ? maxarenas 《《 1 : INITIAL_ARENA_OBJECTS;

。..

}

// 從unused_arena_objects中取出一個未使用的arena_object

arenaobj = unused_arena_objects;

unused_arena_objects = arenaobj-》nextarena;

// 建立arena_object和pool的聯(lián)系

arenaobj-》address = (uptr)address;

。..

return arenaobj;

}

內存池全景圖

375cb0cc-9b2f-11eb-8b86-12bb97331649.png

內存池全景圖

四。 Nginx內存管理機制

在介紹Nginx內存管理之前,先參照Nginx實現(xiàn)一個簡單的內存池,結構圖如下:

3779025e-9b2f-11eb-8b86-12bb97331649.png

其中,mp_pool_s為內存池的結構體頭,包含內存池的一些全局信息,block為小塊內存塊,每一個block有一個mp_node_s結構體,也即mp_pool_s通過鏈表將所有的block連接起來進行管理,而大塊內存由mp_large_s進行分配。申明的數(shù)據(jù)結構如下:

// 結構體

// 大塊內存結構體

struct mp_large_s {

struct mp_large_s *next;

void *alloc;

};

// 小塊內存節(jié)點,小塊內存構成一個鏈表

struct mp_node_s {

unsigned char *last; // 下一次內存從此分配

unsigned char *end; // 內存池結束位置

struct mp_node_s *next; // 指向下一個內存塊

size_t failed; // 改內存塊/node分配失敗的次數(shù)

};

// 內存池結構

struct mp_pool_s {

size_t max; // 能直接從內存池中申請的最大內存,超過需要走大塊內存申請邏輯

struct mp_node_s *current; // 當前分配的node

struct mp_large_s *large; // 大塊內存結構體

struct mp_node_s head[0]; // 柔性數(shù)組不占用大小,其地址為緊挨著結構體的第一個node

};

// 需要實現(xiàn)的接口

struct mp_pool_s *mp_create_pool(size_t size); // 創(chuàng)建內存池

void mp_destory_pool(struct mp_pool_s *pool); // 銷毀內存池

void *mp_alloc(struct mp_pool_s *pool, size_t size); // 分配內存 對齊

void mp_free(struct mp_pool_s *pool, void *p); // 釋放p節(jié)點內存

接下來介紹接口實現(xiàn),先介紹一個接口函數(shù)posix_memalign,函數(shù)原型如下:

int posix_memalign(void**memptr, size_t alignment, size_t size);

/* memptr: 分配好的內存空間的首地址

alignment: 對齊邊界,Linux中32位系統(tǒng)8字節(jié),64位系統(tǒng)16字節(jié),必須為2的冪

size: 指定分配size字節(jié)大小的內存

*/

其功能類似malloc,不過其申請的內存都是對齊的。

內存池相關接口實現(xiàn)如下(只貼出部分代碼,完整代碼私信我)

// 創(chuàng)建并初始化內存池

struct mp_pool_s *mp_create_pool(size_t size) {

struct mp_pool_s *p;

// 分配內存池內存:mp_pool_s + mp_node_s + size

int ret = posix_memalign((void**)&p), MP_ALIGNMENT, size + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s));

if (ret) { return NULL; }

// 可從內存池申請的最大內存

p-》max = (size 《 MP_MAX_ALLOC_FROM_POOL) ? size : MP_MAX_ALLOC_FROM_POOL;

p-》current = p-》head; // 當前可分配的第一個節(jié)點mp_node_s

//一些初始化工作

return p;

}

// 銷毀內存池

void mp_destroy_pool(struct mp_pool_s *pool) {

struct mp_node_s *h, *n;

struct mp_large_s *l;

// 銷毀大塊內存

for (l = pool-》large; l; l = l-》next) { /*.。.*/}

// 銷毀小塊內存

h = pool-》head-》next;

while (h) {/*.。.*/}

free(pool);

}

// mp_alloc 分配內存

void *mp_alloc(struct mp_pool_s *pool, size_t size) {

if (size 《= pool-》max) { // 小塊內存分配

p = pool-》current;

do {

/*.。.不斷尋找下一個可用節(jié)點*/

p = p-》next; // 不夠則找下一個節(jié)點

} while (p);

// 內存池中所有節(jié)點內存都不以滿足分配size內存,需要再次分配一個block

return mp_alloc_block(pool, size);

}

return mp_alloc_large(pool, size); // 大塊內存分配

}

// 大塊節(jié)點內存釋放

void mp_free(struct mp_pool_s *pool, void *p) {

struct mp_large_s *l;

for (l = pool-》large; l; l = l-》next) {

if (p == l-》alloc) {

free(l-》alloc);

//。..

}

}

}

有了上面簡化版,接下來看Nginx中內存管理就比較清晰的,其原理跟上述內存池一致,先上一張圖:

38088afa-9b2f-11eb-8b86-12bb97331649.png

Nginx內存池結構

以下為Nginx實現(xiàn),源代碼主要在src/core/ngx_palloc.h/c兩個文件中

// 內存塊結構體,每個內存塊都有,在最開頭的部分,管理本塊內存

typedef struct {

u_char *last; // 可用內存的起始位置,小塊內存每次都從這里分配

u_char *end; // 可用內存的結束位置

ngx_pool_t *next; // 寫一個內存池節(jié)點

ngx_unit_t failed; // 本節(jié)點分配失敗次數(shù),超過4次,認為本節(jié)點滿,不參與分配,滿的內存塊也不會主動回收

}ngx_pool_data_t;

// 大塊內存節(jié)點

typedef struct ngx_pool_large_s ngx_pool_large_t;

struct ngx_pool_large_s {

ngx_pool_large_t *next; // 多塊大內存串成鏈表,方便回收利用

void *alloc; // 指向malloc分配的大塊內存

};

// nginx內存池結構體

// 多個節(jié)點串成的單向鏈表,每個節(jié)點分配小塊內存

// max,current,大塊內存鏈表旨在頭節(jié)點

// 64位系統(tǒng)大小位80字節(jié),結構體沒有保存內存塊大小的字段,由d.end - p得到

struct ngx_pool_s {

// 本內存節(jié)點信息

ngx_pool_data_t d;

// 下面的字段旨在第一個塊中有意義

size_t max; // 塊最大內存

ngx_pool_t *current; // 當前使用的內存池節(jié)點

ngx_chain_t *chain;

ngx_pool_large_t *large; // 大塊內存

ngx_pool_cleanup_t *cleanup; // 清理鏈表頭指針

ngx_log_t *log;

};

// 創(chuàng)建內存池

ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);

// 銷毀內存池

// 調用清理函數(shù)鏈表,檢查大塊內存鏈表,直接free,遍歷內存池節(jié)點,逐個free

void ngx_destroy_pool(ngx_pool_t *pool);

// 重置內存池,釋放內存,但不歸還系統(tǒng)

// 之前分配的內存塊依舊保留,重置空閑指針位置

void ngx_reset_pool(ngx_pool_t *pool);

// 分配內存 8字節(jié)對齊,速度快,少量浪費 》4k則直接malloc分配大塊內存

void *ngx_palloc(ngx_pool_t *pool, size_t size);

void *ngx_pnalloc(ngx_pool_t *pool, size_t size); // 不對齊

void *ngx_pcalloc(ngx_pool_t *pool, size_t size); // 對齊分配,且初始化

// 大塊內存free

ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);

五。 Ringbuffer環(huán)形緩沖區(qū)機制Ringbuffer的兩個特性:1)先進先出;2)緩沖區(qū)用完,會回卷,丟棄久遠數(shù)據(jù),保存新數(shù)據(jù)。其結構如下圖:

381e728e-9b2f-11eb-8b86-12bb97331649.png

Ringbuffer結構

Ringbuffer的好處:1)減少內存分配進而減少系統(tǒng)調用開銷;2)減少內存碎片,利于程序長期穩(wěn)定運行。

應用場景:服務端程序收到多個客戶端網絡數(shù)據(jù)流時,可先暫存在Ringbuffer,等收到一個完整數(shù)據(jù)包時再讀取。

Linux 5.1合入了一個新的異步IO框架和實現(xiàn):io_uring, io_uring設計了一對共享的RingBuffer用于應用和內核之間的通信,其中,針對提交隊列(SQ),應用是IO提交的生產者(producer),內核是消費者(consumer);反過來,針對完成隊列(CQ),內核是完成事件的生產者,應用是消費者。

以下為一份簡單Ringbuffer實現(xiàn):

// ringbuffer.c

#define BUFFER_SIZE 16 // 緩沖區(qū)的長度

static u32 validLen; // 已使用的數(shù)據(jù)長度

static u8* pHead = NULL; // 環(huán)形存儲區(qū)的首地址

static u8* pTail = NULL; // 環(huán)形存儲區(qū)的尾地址

static u8* pValid = NULL; // 已使用的緩沖區(qū)首地址

static u8* pValidTail = NULL; // 已使用的緩沖區(qū)尾地址

// 初始化環(huán)形緩沖區(qū)

void init Ringbuffer(void) {

if (pHead == NULL) pHead = (u8*)malloc(BUFFER_SIZE);

pValid = pValidTail = pHead;

pTail = pHead + BUFFER_SIZE;

validLen = 0;

}

// 向緩沖區(qū)寫入數(shù)據(jù),buffer寫入數(shù)據(jù)指針,addLen寫入數(shù)據(jù)長度

int writeRingbuffer(u8* buffer, u32 addLen) {

// 將數(shù)據(jù)copy到pValidTail處

if (pValidTail + addLen 》 pTail) // ringbuffer回卷

{

int len1 = addLen - pValidTail;

int len2 = addLen - len1;

memcpy(pValidTail, buffer, len1);

memcpy(pHead, buffer + len1, len2);

pValidTail = pHead + len2; // 新的有效數(shù)據(jù)區(qū)結尾指針

} else {

memcpy(pValidTail, buffer, addLen);

pValidTail += addLen; // 新的有效數(shù)據(jù)結尾指針

}

// 重新計算已使用區(qū)的起始位置

if (validLen + addLen 》 BUFFER_SIZE) {

int moveLen = validLen + addLen - BUFFER_SIZE; // 有效指針將要移動的長度

if (pValid + moveLen 》 pTail) {

int len1 = pTail - pValid;

int len2 = moveLen - len1;

pValid = pHead + len2;

} else {

pValid = pValid + moveLen;

}

validLen = BUFFER_SIZE;

}else {

validLen += addLen;

}

return 0;

}

// 從緩沖區(qū)內取出數(shù)據(jù),buffer讀取數(shù)據(jù)的buffer,len長度

int readRingBuffer(u8* buffer, u32 len)

{

if (len 》 validLen) len = validLen;

if (pValid + len 》 pTail) { // 回卷

int len1 = pTail - pValid;

int len2 = len - len1;

memcpy(buffer, pValid, len1);

memcpy(buffer + len1, pHead, len2);

pValid = pHead + len2;

} else {

memcpy(buffer, pValid, len);

pValid = pValid + len;

}

validLen -= len;

return len;

}

六。 TCMalloc(Thread-Caching Malloc)

內存分配器以下Tcmalloc和Andriod內存管理這兩部分只做簡單介紹。

tcmalloc優(yōu)點:內存分配效率高,運行速度快,穩(wěn)定性強,能夠有效降低系統(tǒng)負載;

應用場景:多核,高并發(fā),多線程

tcmalloc內存申請流程:

ThreadCache對象不夠,就從CentralCache中批量申請

CentralCache不夠,從PageHeap申請Span

PageHeap沒有適合的Page,則向操作系統(tǒng)申請

tcmalloc釋放流程:

ThreadCache釋放對象積累到一定程度,就釋放給CentralCache

CentralCache中一個Span釋放完全了,則把這個Span歸還給PageHeap

PageHeap發(fā)現(xiàn)一批連續(xù)的Page都釋放了,則歸還給操作系統(tǒng)

多個連續(xù)的Page組成Span, Span 中記錄起始 Page 的編號,以及 Page 數(shù)量,大對象(》32k)直接分配Span,小對象(《=32k)在Span中分配Object。以下為上述結構圖解:

3850eb74-9b2f-11eb-8b86-12bb97331649.png

ThreadCache

?。跜entralCache](/Users/zhongrunkang/Library/Application Support/typora-user-images/image-20210228203604225.png)

38b64d48-9b2f-11eb-8b86-12bb97331649.png

PageHeap

七。 Andriod內存管理機制

Q:Andriod的Java程序為什么容易出現(xiàn)OOM?

A:因為Andriod系統(tǒng)堆Dalvik的VM HeapSize做了硬性限制,當java進程申請的java空間超過閾值時,就會拋出OOM,這樣設計的目的是為了讓比較多的進程常駐內存,這樣程序啟動時就不用每次都重新加載到內存,能夠給用戶更快的響應。

Andriod系統(tǒng)中的應用程序基本都是Java進程。

Andriod內存管理機制

分配機制:

為每一個進程分配一個合理大小的內存塊,保證每個進程能夠正常運行,同時確保進程不會占用太多的內存;Andriod系統(tǒng)需要最大限度的讓更多進程存活在內存中,以保證用戶再次打開應用時減少應用的啟動時間,提高用戶體驗。

回收機制:

當系統(tǒng)內存不足時,需要一個合理的回收再分配機制,以保證新的進程可以正常運行?;厥諘r殺死那些正在占用內存的進程,OS需要提供一個合理的殺死進程機制。
編輯:lyn

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11350

    瀏覽量

    210462
  • 內存管理
    +關注

    關注

    0

    文章

    168

    瀏覽量

    14193
  • python
    +關注

    關注

    56

    文章

    4809

    瀏覽量

    85053
  • nginx
    +關注

    關注

    0

    文章

    154

    瀏覽量

    12238

原文標題:一文淺析內存管理機制

文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    hyper 內存,Hyper內存:如何監(jiān)控與優(yōu)化hyper-v虛擬機的內存使用

    在日常工作中,我們常常需要處理大量的文件和數(shù)據(jù),這些重復性任務不僅耗時耗力,還容易因疲勞而導致錯誤。幸運的是,批量管理工具的出現(xiàn)為這一問題提供了高效的解決方案。今天就為大家介紹Hyper內存
    的頭像 發(fā)表于 01-24 14:15 ?178次閱讀
    hyper <b class='flag-5'>內存</b>,Hyper<b class='flag-5'>內存</b>:如何監(jiān)控與優(yōu)化hyper-v<b class='flag-5'>虛擬</b>機的<b class='flag-5'>內存</b>使用

    虛擬內存和云計算的關系

    虛擬內存是一種計算機系統(tǒng)內存管理技術,它通過將物理內存與磁盤空間結合起來,使得應用程序可以訪問比物理
    的頭像 發(fā)表于 12-04 09:50 ?179次閱讀

    虛擬內存溢出該怎么處理 虛擬內存在服務器中的應用

    在現(xiàn)代計算機系統(tǒng)中,虛擬內存是一種重要的資源管理技術,它允許系統(tǒng)使用硬盤空間來擴展物理內存的容量。然而,當系統(tǒng)運行的程序和進程超出了物理
    的頭像 發(fā)表于 12-04 09:49 ?291次閱讀

    Linux下如何管理虛擬內存 使用虛擬內存時的常見問題

    在Linux系統(tǒng)中,虛擬內存管理是操作系統(tǒng)內核的一個重要功能,負責管理物理內存和磁盤上的交換空間。以下是對Linux下如何
    的頭像 發(fā)表于 12-04 09:19 ?634次閱讀

    虛擬內存對計算機性能的影響

    在現(xiàn)代計算機系統(tǒng)中,內存管理是確保系統(tǒng)高效運行的關鍵因素之一。虛擬內存技術作為內存管理的核心組成部分,對于提升計算機性能和用戶體驗起著至關重
    的頭像 發(fā)表于 12-04 09:17 ?1271次閱讀

    什么是虛擬內存分頁 Windows系統(tǒng)虛擬內存優(yōu)化方法

    虛擬內存分頁概述 在Windows操作系統(tǒng)中,虛擬內存是通過分頁機制實現(xiàn)的。分頁允許系統(tǒng)將內存中的數(shù)據(jù)移動到硬盤上,以便為當前運行的程序騰出空間。這個過程對于保持系統(tǒng)的流暢運行至關重要
    的頭像 發(fā)表于 12-04 09:16 ?613次閱讀

    虛擬內存不足如何解決 虛擬內存物理內存的區(qū)別

    虛擬內存不足的解決方案 虛擬內存不足是計算機用戶經常遇到的問題,尤其是在運行大型軟件或多任務處理時。以下是一些解決虛擬內存不足問題的方法: 增加物理
    的頭像 發(fā)表于 12-04 09:14 ?598次閱讀

    虛擬內存的作用和原理 如何調整虛擬內存設置

    虛擬內存,也稱為虛擬內存管理或頁面文件,是計算機操作系統(tǒng)中的一種內存管理技術。它允許系統(tǒng)使用硬盤空間作為額外的RAM(隨機存取存儲器),以彌
    的頭像 發(fā)表于 12-04 09:13 ?803次閱讀

    如何優(yōu)化RAM內存使用

    :使用任務管理器查看當前運行的程序和服務,關閉那些不需要的。 禁用啟動程序 :減少開機啟動項,只保留必要的程序。 2. 優(yōu)化操作系統(tǒng)設置 調整虛擬內存 :合理設置虛擬內存,避免過多占用硬盤空間。 清理磁盤 :定期進行磁盤清理,
    的頭像 發(fā)表于 11-11 09:58 ?631次閱讀

    Windows管理內存的三種主要方式

    Windows操作系統(tǒng)提供了多種方式來管理內存,以確保系統(tǒng)資源的有效利用和性能的優(yōu)化。以下是關于Windows管理內存的三種主要方式的詳細闡述,包括堆
    的頭像 發(fā)表于 10-12 17:09 ?1390次閱讀

    邏輯內存物理內存的區(qū)別

    邏輯內存物理內存是計算機系統(tǒng)中兩個重要的概念,它們在計算機的運行和數(shù)據(jù)處理中起著至關重要的作用。 1. 物理內存(Physical Mem
    的頭像 發(fā)表于 09-27 15:38 ?941次閱讀

    內存緩沖區(qū)和內存關系

    內存緩沖區(qū)和內存之間的關系是計算機體系結構中一個至關重要的方面,它們共同協(xié)作以提高數(shù)據(jù)處理的效率和系統(tǒng)的整體性能。
    的頭像 發(fā)表于 09-10 14:38 ?779次閱讀

    深入理解Java 8內存管理機制及故障排查實戰(zhàn)指南

    Java的自動內存管理機制是由 JVM 中的垃圾收集器來實現(xiàn)的,垃圾收集器會定期掃描堆內存中的對象,檢測并清除不再使用的對象,以釋放內存資源。
    的頭像 發(fā)表于 04-04 08:10 ?1065次閱讀
    深入理解Java 8<b class='flag-5'>內存</b><b class='flag-5'>管理機制</b>及故障排查實戰(zhàn)指南

    物理內存模型的演變

    內存管理概述中,主要是以Linux v2.6.11為例進行分析的,但是計算技術在不斷發(fā)展,新的存儲架構、新的指令集架構、新的SoC架構等都對物理內存模型的抽象提出了更高要求。為此,必須
    的頭像 發(fā)表于 02-25 10:35 ?540次閱讀

    Linux內核內存管理之內核非連續(xù)物理內存分配

    我們已經知道,最好將虛擬地址映射到連續(xù)頁幀,從而更好地利用緩存并實現(xiàn)更低的平均內存訪問時間。然而,如果對內存區(qū)域的請求并不頻繁,那么考慮基于通過連續(xù)線性地址訪問非連續(xù)頁幀的分配方案是有意義的。該模式
    的頭像 發(fā)表于 02-23 09:44 ?1082次閱讀
    Linux內核<b class='flag-5'>內存</b><b class='flag-5'>管理</b>之內核非連續(xù)<b class='flag-5'>物理</b><b class='flag-5'>內存</b>分配