MySQL內(nèi)存管理機(jī)制淺析
一、placement new的定義
通常情況下,C++中通過用new方式申請內(nèi)存空間時(shí),是在系統(tǒng)的堆內(nèi)存空間中進(jìn)行分配,底層使用C標(biāo)準(zhǔn)庫的malloc()完成內(nèi)存分配工作。
因此本次申請的內(nèi)存空間大小,是根據(jù)程序運(yùn)行時(shí)對象的大小及使用情況來決定的。
但是某些場景中,可能需要預(yù)先分配完成內(nèi)存空間,然后再把對象"放置"在之前預(yù)先分配的內(nèi)存空間上。即所謂的placement new操作。
定點(diǎn)放置的new操作的語法不同于普通的new操作,比如:我們一般在堆中申請內(nèi)存空間,通常寫:
Object* o = new Object();
而定點(diǎn)放置new的語法形式為:
Object* o = new (pointer) Object();
這里的pointer就是預(yù)先分配好內(nèi)存塊的首地址。
二、placement new使用場景
傳統(tǒng)堆分配內(nèi)存方式的弊端:通過new操作符進(jìn)行堆內(nèi)存的分配,操作系統(tǒng)需要在堆中找到連續(xù)且大小符合要求的內(nèi)存空間,這個(gè)查詢匹配的效率是低下的。
極端情況下可能由于空間不足,導(dǎo)致出現(xiàn)內(nèi)存分配失敗的問題發(fā)生。
placement new分配方式:創(chuàng)建的對象都在預(yù)先分配好的內(nèi)存緩沖區(qū)中操作,無需查詢及匹配內(nèi)存空間,內(nèi)存分配的時(shí)間是常量O(1)。
由于在之前預(yù)留的內(nèi)存空間進(jìn)行分配,因此不會出現(xiàn)程序運(yùn)行時(shí)由于內(nèi)存空間不足,導(dǎo)致內(nèi)存分配失敗的問題。
三、placement new和 MySQL 內(nèi)存管理機(jī)制的關(guān)系
正是由于上述placement new的機(jī)制特性,因此其非常適合那些對時(shí)間,性能要求高,長時(shí)間運(yùn)行,不希望被中斷的應(yīng)用程序。例如數(shù)據(jù)庫這類的應(yīng)用場景,就是很好的例子。
MySQL內(nèi)部使用mem_root進(jìn)行內(nèi)存管理,可以實(shí)現(xiàn)多次批量的內(nèi)存空間申請,并且可以把對象放置到mem_root定義的內(nèi)存空間中,這樣程序運(yùn)行失敗或者中途異常crash退出,我們就無需關(guān)心是否成功釋放內(nèi)存。
一切都通過mem_root進(jìn)行代理操作,整個(gè)內(nèi)存的申請、分配、回收過程透明完成。
四、MySQL中 mem_root 使用場景
//聲明 mem_root 對象
MEM_ROOT execute_mem_root;
Query_arena execute_arena(&execute_mem_root,Query_arena::STMT_INITIALIZED_FOR_SP);
//預(yù)分配內(nèi)存塊空間
init_sql_alloc(key_memory_sp_head_execute_root, &mem_root,
MEM_ROOT_BLOCK_SIZE, 0);
//把thd中的mem_root指針指向execute_mem_root對應(yīng)的內(nèi)存塊
thd->swap_query_arena(execute_arena, &backup_arena);
//把對象分配在預(yù)先申請的mem_root上
LEX *sublex = new (thd->mem_root) st_lex_local;
//一些邏輯計(jì)算操作
......
//釋放表空間
free_root(&execute_mem_root, MYF(0));
總結(jié):MySQL通過mem_root進(jìn)行內(nèi)存的統(tǒng)一申請、回收、管理。不但提升了內(nèi)存分配的效率,提高了系統(tǒng)資源的利用率,而且減少了內(nèi)存碎片化,是MySQL性能提升的一個(gè)重要抓手。
Enjoy GreatSQL :)?