久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

Linux虛擬內(nèi)存管理方法是什么

168次閱讀
沒有評論

共計 5936 個字符,預(yù)計需要花費 15 分鐘才能閱讀完成。

本篇內(nèi)容介紹了“Linux 虛擬內(nèi)存管理方法是什么”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓丸趣 TV 小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

五.free 的內(nèi)存真的釋放了嗎(還給 OS)?

前面所有例子都有一個很嚴重的問題,就是分配的內(nèi)存都沒有釋放,即導(dǎo)致內(nèi)存泄露。原則上所有 malloc/new 分配的內(nèi)存,都需 free/delete 來釋放。但是,free 了的內(nèi)存真的釋放了嗎?

要說清楚這個問題,可通過下面例子來說明。

初始狀態(tài):如圖 (1) 所示,系統(tǒng)已分配 ABCD 四塊內(nèi)存,其中 ABD 在堆內(nèi)分配,C 使用 mmap 分配。為簡單起見,圖中忽略了如共享庫等文件映射區(qū)域的地址空間。

E=malloc(100k):分配 100k 內(nèi)存,小于 128k,從堆內(nèi)分配,堆內(nèi)剩余空間不足,擴展堆頂 (brk) 指針。

free(A):釋放 A 的內(nèi)存,在 glibc 中,僅僅是標記為可用,形成一個內(nèi)存空洞 (碎片),并沒有真正釋放。如果此時需要分配 40k 以內(nèi)的空間,可重用此空間,剩余空間形成新的小碎片。

free(C):C 空間大于 128K,使用 mmap 分配,如果釋放 C,會調(diào)用 munmap 系統(tǒng)調(diào)用來釋放,并會真正釋放該空間,還給 OS,如圖 (4) 所示。

free(D):與釋放 A 類似,釋放 D 同樣會導(dǎo)致一個空洞,獲得空閑空間,但并不會還給 OS。此時,空閑總空間為 100K,但由于虛擬地址不連續(xù),無法合并,空閑空間無法滿足大于 60k 的分配請求。

free(E):釋放 E,由于與 D 連續(xù),兩者將進行合并,得到 160k 連續(xù)空閑空間。同時 E 是最靠近堆頂?shù)目臻g,glibc 的 free 實現(xiàn)中,只要堆頂附近釋放總空間(包括合并的空間)超過 128k,即會調(diào)用 sbrk(-SIZE) 來回溯堆頂指針,將原堆頂空間還給 OS,如圖 (6) 所示。而堆內(nèi)的空閑空間還是不會歸還 OS 的。

由此可見:

malloc 使用 mmap 分配的內(nèi)存 (大于 128k),free 會調(diào)用 munmap 系統(tǒng)調(diào)用馬上還給 OS,實現(xiàn)真正釋放。

堆內(nèi)的內(nèi)存,只有釋放堆頂?shù)目臻g,同時堆頂總連續(xù)空閑空間大于 128k 才使用 sbrk(-SIZE) 回收內(nèi)存,真正歸還 OS。

堆內(nèi)的空閑空間,是不會歸還給 OS 的。

六. 程序代碼中 malloc 的內(nèi)存都有相應(yīng)的 free,就不會出現(xiàn)內(nèi)存泄露了嗎?

狹義上的內(nèi)存泄露是指 malloc 的內(nèi)存,沒有 free,導(dǎo)致內(nèi)存浪費,直到程序結(jié)束。而廣義上的內(nèi)存泄露就是進程使用內(nèi)存量不斷增加,或大大超出系統(tǒng)原設(shè)計的上限。

上一節(jié)說到,free 了的內(nèi)存并不會馬上歸還 OS,并且堆內(nèi)的空洞(碎片)更是很難真正釋放,除非空洞成為了新的堆頂。所以,如上一例子情況 (5),釋放了 40k 和 60k 兩片內(nèi)存,但如果此時需要申請大于 60k(如 70k),沒有可用碎片,必須向 OS 申請,實際使用內(nèi)存仍然增大。

因此,隨著系統(tǒng)頻繁地 malloc 和 free,尤其對于小塊內(nèi)存,堆內(nèi)將產(chǎn)生越來越多不可用的碎片,導(dǎo)致“內(nèi)存泄露”。而這種“泄露”現(xiàn)象使用 valgrind 是無法檢測出來的。

下圖是 MySQL 存在大量分區(qū)表時的內(nèi)存使用情況 (RSS 和 VSZ),疑似“內(nèi)存泄露”。

因此,當我們寫程序時,不能完全依賴 glibc 的 malloc 和 free 的實現(xiàn)。更好方式是建立屬于進程的內(nèi)存池,即一次分配 (malloc) 大塊內(nèi)存,小內(nèi)存從內(nèi)存池中獲得,當進程結(jié)束或該塊內(nèi)存不可用時,一次釋放 (free),可大大減少碎片的產(chǎn)生。

七. 既然堆內(nèi)內(nèi)存不能直接釋放,為什么不全部使用 mmap 來分配?

由于堆內(nèi)碎片不能直接釋放,而問題 5 中說到 mmap 分配的內(nèi)存可以會通過 munmap 進行 free,實現(xiàn)真正釋放。既然堆內(nèi)碎片不能直接釋放,導(dǎo)致疑似“內(nèi)存泄露”問題,為什么 malloc 不全部使用 mmap 來實現(xiàn)呢?而僅僅對于大于 128k 的大塊內(nèi)存才使用 mmap?

其實,進程向 OS 申請和釋放地址空間的接口 sbrk/mmap/munmap 都是系統(tǒng)調(diào)用,頻繁調(diào)用系統(tǒng)調(diào)用都比較消耗系統(tǒng)資源的。并且,mmap 申請的內(nèi)存被 munmap 后,重新申請會產(chǎn)生更多的缺頁中斷。例如使用 mmap 分配 1M 空間,第一次調(diào)用產(chǎn)生了大量缺頁中斷 (1M/4K 次),當 munmap 后再次分配 1M 空間,會再次產(chǎn)生大量缺頁中斷。缺頁中斷是內(nèi)核行為,會導(dǎo)致內(nèi)核態(tài) CPU 消耗較大。另外,如果使用 mmap 分配小內(nèi)存,會導(dǎo)致地址空間的分片更多,內(nèi)核的管理負擔更大。

而堆是一個連續(xù)空間,并且堆內(nèi)碎片由于沒有歸還 OS,如果可重用碎片,再次訪問該內(nèi)存很可能不需產(chǎn)生任何系統(tǒng)調(diào)用和缺頁中斷,這將大大降低 CPU 的消耗。

因此,glibc 的 malloc 實現(xiàn)中,充分考慮了 sbrk 和 mmap 行為上的差異及優(yōu)缺點,默認分配大塊內(nèi)存 (128k) 才使用 mmap 獲得地址空間,也可通過  mallopt(M_MMAP_THRESHOLD, SIZE) 來修改這個臨界值。

八. 如何查看進程的缺頁中斷信息?

可通過以下命令查看缺頁中斷信息

ps -o majflt,minflt -C  program_name 
ps -o majflt,minflt -p  pid

其中,majflt 代表 major fault,指大錯誤,minflt 代表 minor fault,指小錯誤。這兩個數(shù)值表示一個進程自啟動以來所發(fā)生的缺頁中斷的次數(shù)。其中 majflt 與 minflt 的不同是,majflt 表示需要讀寫磁盤,可能是內(nèi)存對應(yīng)頁面在磁盤中需要 load 到物理內(nèi)存中,也可能是此時物理內(nèi)存不足,需要淘汰部分物理頁面至磁盤中。

例如,下面是 mysqld 的一個例子。

mysql@ TLOG_590_591:~  ps -o majflt,minflt -C mysqld
MAJFLT MINFLT
144856 15296294

如果進程的內(nèi)核態(tài) CPU 使用過多,其中一個原因就可能是單位時間的缺頁中斷次數(shù)多個,可通過以上命令來查看。

如果 MAJFLT 過大,很可能是內(nèi)存不足。

如果 MINFLT 過大,很可能是頻繁分配 / 釋放大塊內(nèi)存 (128k),malloc 使用 mmap 來分配。對于這種情況,可通過  mallopt(M_MMAP_THRESHOLD, SIZE) 增大臨界值,或程序?qū)崿F(xiàn)內(nèi)存池。

九. 如何查看堆內(nèi)內(nèi)存的碎片情況?

glibc 提供了以下結(jié)構(gòu)和接口來查看堆內(nèi)內(nèi)存和 mmap 的使用情況。

struct mallinfo {
 int arena; /* non-mmapped space allocated from system */
 int ordblks; /* number of free chunks */
 int smblks; /* number of fastbin blocks */
 int hblks; /* number of mmapped regions */
 int hblkhd; /* space in mmapped regions */
 int usmblks; /* maximum total allocated space */
 int fsmblks; /* space available in freed fastbin blocks */
 int uordblks; /* total allocated space */
 int fordblks; /* total free space */
 int keepcost; /* top-most, releasable (via malloc_trim) space */

/*  返回  heap(main_arena)  的內(nèi)存使用情況,以  mallinfo  結(jié)構(gòu)返回  */ struct mallinfo mallinfo(); /*  將  heap  和  mmap  的使用情況輸出到  stderr */ void malloc_stats();

可通過以下例子來驗證 mallinfo 和 malloc_stats 輸出結(jié)果。

#include  stdlib.h 
#include  stdio.h 
#include  string.h 
#include  unistd.h 
#include  sys/mman.h 
#include  malloc.h 
size_t heap_malloc_total, heap_free_total,
 mmap_total, mmap_count;
void print_info()
 struct mallinfo mi = mallinfo();
 printf( count by itself:\n 
 printf( \theap_malloc_total=%lu heap_free_total=%lu heap_in_use=%lu\n\
\tmmap_total=%lu mmap_count=%lu\n ,
 heap_malloc_total*1024, heap_free_total*1024, heap_malloc_total*1024 - heap_free_total*1024,
 mmap_total*1024, mmap_count);
 printf( count by mallinfo:\n 
 printf( \theap_malloc_total=%lu heap_free_total=%lu heap_in_use=%lu\n\
\tmmap_total=%lu mmap_count=%lu\n ,
 mi.arena, mi.fordblks, mi.uordblks,
 mi.hblkhd, mi.hblks);
 printf( from malloc_stats:\n 
 malloc_stats();
#define ARRAY_SIZE 200
int main(int argc, char** argv)
 char** ptr_arr[ARRAY_SIZE];
 int i;
 for( i = 0; i   ARRAY_SIZE; i++) { ptr_arr[i] = malloc(i * 1024);
 if ( i   128)
 heap_malloc_total += i;
 else {
 mmap_total += i;
 mmap_count++;
 }
 }
 print_info();
 for( i = 0; i   ARRAY_SIZE; i++) { if ( i % 2 == 0)
 continue;
 free(ptr_arr[i]);
 if ( i   128)
 heap_free_total += i;
 else {
 mmap_total -= i;
 mmap_count--;
 }
 }
 printf( \nafter free\n 
 print_info();
 return 1;
}

該例子第一個循環(huán)為指針數(shù)組每個成員分配索引位置 (KB) 大小的內(nèi)存塊,并通過 128 為分界分別對 heap 和 mmap 內(nèi)存分配情況進行計數(shù);第二個循環(huán)是 free 索引下標為奇數(shù)的項,同時更新計數(shù)情況。通過程序的計數(shù)與 mallinfo/malloc_stats 接口得到結(jié)果進行對比,并通過 print_info 打印到終端。

下面是一個執(zhí)行結(jié)果:

count by itself:
 heap_malloc_total=8323072 heap_free_total=0 heap_in_use=8323072
 mmap_total=12054528 mmap_count=72
count by mallinfo:
 heap_malloc_total=8327168 heap_free_total=2032 heap_in_use=8325136
 mmap_total=12238848 mmap_count=72
from malloc_stats:
Arena 0:
system bytes = 8327168
in use bytes = 8325136
Total (incl. mmap):
system bytes = 20566016
in use bytes = 20563984
max mmap regions = 72
max mmap bytes = 12238848
after free
count by itself:
 heap_malloc_total=8323072 heap_free_total=4194304 heap_in_use=4128768
 mmap_total=6008832 mmap_count=36
count by mallinfo:
 heap_malloc_total=8327168 heap_free_total=4197360 heap_in_use=4129808
 mmap_total=6119424 mmap_count=36
from malloc_stats:
Arena 0:
system bytes = 8327168
in use bytes = 4129808
Total (incl. mmap):
system bytes = 14446592
in use bytes = 10249232
max mmap regions = 72
max mmap bytes = 12238848

由上可知,程序統(tǒng)計和 mallinfo 得到的信息基本吻合,其中 heap_free_total 表示堆內(nèi)已釋放的內(nèi)存碎片總和。

如果想知道堆內(nèi)片究竟有多碎,可通過 mallinfo 結(jié)構(gòu)中的 fsmblks、smblks、ordblks 值得到,這些值表示不同大小區(qū)間的碎片總個數(shù),這些區(qū)間分別是 0~80 字節(jié),80~512 字節(jié),512~128k。如果 fsmblks、smblks 的值過大,那碎片問題可能比較嚴重了。

不過,mallinfo 結(jié)構(gòu)有一個很致命的問題,就是其成員定義全部都是 int,在 64 位環(huán)境中,其結(jié)構(gòu)中的 uordblks/fordblks/arena/usmblks 很容易就會導(dǎo)致溢出,應(yīng)該是歷史遺留問題,使用時要注意!

十. 除了 glibc 的 malloc/free,還有其他第三方實現(xiàn)嗎?

其實,很多人開始詬病 glibc 內(nèi)存管理的實現(xiàn),就是在高并發(fā)性能低下和內(nèi)存碎片化問題都比較嚴重,因此,陸續(xù)出現(xiàn)一些第三方工具來替換 glibc 的實現(xiàn),最著名的當屬 google 的 tcmalloc 和 facebook 的 jemalloc。

“Linux 虛擬內(nèi)存管理方法是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注丸趣 TV 網(wǎng)站,丸趣 TV 小編將為大家輸出更多高質(zhì)量的實用文章!

正文完
 
丸趣
版權(quán)聲明:本站原創(chuàng)文章,由 丸趣 2023-08-16發(fā)表,共計5936字。
轉(zhuǎn)載說明:除特殊說明外本站除技術(shù)相關(guān)以外文章皆由網(wǎng)絡(luò)搜集發(fā)布,轉(zhuǎn)載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 龙口市| 尼木县| 德保县| 汤原县| 申扎县| 泸水县| 磐石市| 安岳县| 平阴县| 道真| 南华县| 天长市| 维西| 新泰市| 固安县| 临沭县| 重庆市| 锡林郭勒盟| 宜良县| 兰坪| 和林格尔县| 长宁县| 浪卡子县| 黄浦区| 鹤峰县| 镇安县| 屏边| 且末县| 会泽县| 合山市| 昌都县| 封开县| 平罗县| 平昌县| 绩溪县| 昌平区| 衡阳市| 克东县| 永平县| 安仁县| 屯留县|