Linux內(nèi)存優(yōu)化實(shí)戰(zhàn):讓你的服務(wù)器告別卡頓
在數(shù)字化時(shí)代,服務(wù)器的穩(wěn)定運(yùn)行直接關(guān)系到業(yè)務(wù)的興衰。想象一下,電商平臺在促銷活動時(shí),服務(wù)器突然卡頓,頁面加載緩慢,用戶無法順利下單;在線游戲中,玩家頻繁遭遇卡頓,技能釋放延遲,游戲體驗(yàn)大打折扣,最終導(dǎo)致大量用戶流失。這些場景并非虛構(gòu),而是許多企業(yè)正在面臨的嚴(yán)峻問題。
當(dāng)服務(wù)器出現(xiàn)卡頓,內(nèi)存往往成為首要懷疑對象。畢竟,內(nèi)存作為數(shù)據(jù)的臨時(shí)存儲區(qū)域,承擔(dān)著數(shù)據(jù)的快速讀寫任務(wù)。如果內(nèi)存不足,系統(tǒng)就不得不頻繁地進(jìn)行數(shù)據(jù)交換,將內(nèi)存中的數(shù)據(jù)轉(zhuǎn)移到硬盤上,這無疑會大大降低系統(tǒng)的運(yùn)行效率,導(dǎo)致服務(wù)器卡頓。但內(nèi)存問題究竟如何影響服務(wù)器性能?又該如何進(jìn)行優(yōu)化呢?接下來,就讓我們深入 Linux 內(nèi)存世界,一探究竟。
Part1Linux內(nèi)存管理機(jī)制
在對 Linux 系統(tǒng)內(nèi)存管理機(jī)制進(jìn)行深入優(yōu)化之前,我們需要先全面且細(xì)致地了解其基本運(yùn)行機(jī)制,這一必要性恰似醫(yī)生在治病救人前,必須深入透徹地了解人體的生理結(jié)構(gòu)。Linux 系統(tǒng)采用虛擬內(nèi)存系統(tǒng),用戶空間應(yīng)用程序所引用的任何地址,都必須通過底層計(jì)算機(jī)系統(tǒng)中的頁表與地址轉(zhuǎn)換硬件相結(jié)合的方式,轉(zhuǎn)化為物理地址。
在 Linux 環(huán)境下,程序生成的所有內(nèi)存地址都會經(jīng)由處理器中的地址轉(zhuǎn)換機(jī)制進(jìn)行處理,將進(jìn)程特定的虛擬地址轉(zhuǎn)換為物理內(nèi)存地址,這便是虛擬內(nèi)存的工作原理。就如同醫(yī)生只有精準(zhǔn)把握人體生理結(jié)構(gòu),才能準(zhǔn)確診斷病情并制定有效治療方案,我們只有清晰掌握 Linux 內(nèi)存管理的基本機(jī)制,才能在后續(xù)的優(yōu)化工作中找準(zhǔn)方向,高效解決可能出現(xiàn)的內(nèi)存問題,實(shí)現(xiàn)系統(tǒng)性能的提升 。
1.1 用戶空間與內(nèi)核空間內(nèi)存劃分
在 Linux 系統(tǒng)中,每個進(jìn)程都擁有 4GB 的虛擬地址空間(以 32 位系統(tǒng)為例),這就像是一個巨大的倉庫,而這個倉庫被劃分為兩個部分:用戶空間和內(nèi)核空間,它們的比例通常是 3:1 。用戶空間是進(jìn)程存放代碼、數(shù)據(jù)、堆和棧的地方,進(jìn)程在這個空間內(nèi)執(zhí)行用戶態(tài)的程序,就像是在自己的小天地里自由活動,但這個小天地的權(quán)限是有限的。而內(nèi)核空間則是操作系統(tǒng)內(nèi)核的專屬區(qū)域,存放著內(nèi)核代碼和數(shù)據(jù),它擁有最高權(quán)限,可以直接訪問硬件資源,就像是倉庫的管理員,掌控著一切。用戶進(jìn)程通常只能訪問用戶空間的虛擬地址,不能直接訪問內(nèi)核空間。只有通過系統(tǒng)調(diào)用、中斷或異常等方式,進(jìn)程才能從用戶態(tài)切換到內(nèi)核態(tài),進(jìn)入內(nèi)核空間執(zhí)行操作,就像普通人需要通過特定的渠道才能聯(lián)系到倉庫管理員一樣。
圖片
1.2 內(nèi)存分配與回收機(jī)制
內(nèi)存分配就像是在倉庫里分配貨架給不同的商品。當(dāng)進(jìn)程需要內(nèi)存時(shí),它會向操作系統(tǒng)申請。在用戶空間,常用的內(nèi)存分配函數(shù)有malloc、calloc和realloc等。這些函數(shù)通過與操作系統(tǒng)的交互來獲取內(nèi)存。而在內(nèi)核空間,主要有kmalloc、vmalloc和get_free_pages等函數(shù)用于內(nèi)存分配 。kmalloc用于分配較小的連續(xù)物理內(nèi)存塊,就像是分配一個小貨架,保證物理上的連續(xù)性,適合一些對內(nèi)存訪問速度要求較高的場景;vmalloc則用于分配較大的虛擬內(nèi)存,這些內(nèi)存可能在物理上不連續(xù),但在虛擬地址空間上是連續(xù)的,如同分配一個大的、可以拼接的貨架,適合分配大塊內(nèi)存的情況;get_free_pages用于以頁為單位分配連續(xù)的物理內(nèi)存,頁是內(nèi)存管理的基本單位,通常為 4KB,就像是按固定大小的小格子來分配內(nèi)存。
當(dāng)進(jìn)程不再需要內(nèi)存時(shí),就需要進(jìn)行內(nèi)存回收,把貨架歸還給倉庫。在用戶空間,使用free函數(shù)釋放通過malloc等函數(shù)分配的內(nèi)存。內(nèi)核空間中,對應(yīng)的有kfree等函數(shù)用于釋放kmalloc分配的內(nèi)存 。同時(shí),Linux 內(nèi)核還會通過一些后臺線程,如kswapd,定期檢查內(nèi)存的使用情況,回收那些長時(shí)間未被使用的內(nèi)存,將其重新標(biāo)記為可用,就像倉庫管理員定期清理那些長時(shí)間閑置的貨架,以便重新分配給其他需要的人。
當(dāng)我們透徹掌握了 Linux 內(nèi)存管理機(jī)制,諸如虛擬內(nèi)存怎樣巧妙地將物理內(nèi)存與磁盤空間有機(jī)結(jié)合,為應(yīng)用程序拓展出更為廣闊的可用內(nèi)存空間;頁緩存又是如何高效緩存文件系統(tǒng)數(shù)據(jù),從而大幅提升 I/O 性能;slab 分配器怎樣精細(xì)管理內(nèi)核對象緩存等關(guān)鍵要點(diǎn)后,便如同手握一把萬能鑰匙,擁有了深入剖析服務(wù)器內(nèi)存卡頓問題的有力工具 。
Part2精準(zhǔn)定位內(nèi)存問題
在優(yōu)化之前,我們需要先找到內(nèi)存問題的根源,就像醫(yī)生看病需要先診斷病情一樣。下面介紹一些常用的內(nèi)存監(jiān)控工具和內(nèi)存問題的識別方法。
2.1內(nèi)存監(jiān)控工具
①free 命令:這是一個簡單而實(shí)用的命令,用于查看系統(tǒng)內(nèi)存的使用情況,就像是查看倉庫的庫存清單。執(zhí)行free -h(-h參數(shù)表示以人類可讀的格式顯示),輸出結(jié)果如下:
total used free shared buff/cache available
Mem: 7.7G 2.0G 3.5G 112M 2.2G 5.3G
Swap: 2.0G 0B 2.0G- total:表示系統(tǒng)的總內(nèi)存量,這里是 7.7G 。
- used:已使用的內(nèi)存量,為 2.0G 。
- free:完全空閑的內(nèi)存量,3.5G 。
- shared:多個進(jìn)程共享的內(nèi)存量,112M 。
- buff/cache:用于緩存和緩沖區(qū)的內(nèi)存量,2.2G ,這部分內(nèi)存主要用于加速文件系統(tǒng)的讀寫操作,就像倉庫中用于臨時(shí)存放常用物品的區(qū)域,雖然被占用,但在需要時(shí)可以快速釋放供其他進(jìn)程使用。
- available:表示可用于進(jìn)程下一次分配的內(nèi)存量,它是一個估計(jì)值,考慮了內(nèi)存的緩存和緩沖區(qū)等因素,這里是 5.3G ,這個數(shù)值對于判斷系統(tǒng)實(shí)際可用內(nèi)存更有參考價(jià)值。
②top 命令:top 命令可以實(shí)時(shí)動態(tài)地查看系統(tǒng)的整體運(yùn)行情況,包括內(nèi)存使用、CPU 占用等,是系統(tǒng)管理員常用的監(jiān)控工具,就像一個實(shí)時(shí)監(jiān)控系統(tǒng)狀態(tài)的儀表盤。執(zhí)行top命令后,按M鍵可以按照內(nèi)存使用量對進(jìn)程進(jìn)行排序,這樣就能快速找到占用內(nèi)存較多的進(jìn)程 。在 top 命令的輸出中,與內(nèi)存相關(guān)的重要字段有:
- VIRT:進(jìn)程使用的虛擬內(nèi)存總量,單位為 KB ,它包括進(jìn)程的代碼段、數(shù)據(jù)段、堆、棧以及共享庫所占用的內(nèi)存,就像是進(jìn)程申請的倉庫空間總和,不管實(shí)際是否使用。
- RES:進(jìn)程使用的、未被換出的物理內(nèi)存大小,單位為 KB ,這是進(jìn)程真正占用的物理內(nèi)存,即進(jìn)程在倉庫中實(shí)際使用的貨架空間。
- SHR:共享內(nèi)存大小,單位為 KB ,表示多個進(jìn)程共享的內(nèi)存部分,比如共享庫文件所占用的內(nèi)存,就像多個進(jìn)程共同租用的倉庫區(qū)域。
- %MEM:進(jìn)程使用的物理內(nèi)存百分比,直觀地顯示了每個進(jìn)程對物理內(nèi)存的占用比例,幫助我們快速定位內(nèi)存占用大戶。
③vmstat 命令:vmstat 命令可以提供關(guān)于系統(tǒng)虛擬內(nèi)存、進(jìn)程、CPU 活動以及 I/O 操作等方面的統(tǒng)計(jì)信息,幫助我們?nèi)媪私庀到y(tǒng)的性能狀況,就像一個多功能的系統(tǒng)性能檢測儀。執(zhí)行vmstat 1(表示每秒輸出一次統(tǒng)計(jì)信息),輸出結(jié)果的部分字段含義如下:
- swpd:使用的虛擬內(nèi)存量(KB),如果這個值大于 0,說明系統(tǒng)正在使用虛擬內(nèi)存,可能物理內(nèi)存不足。
- free:空閑內(nèi)存量(KB),與 free 命令中的free字段含義相同。
- buff:用作緩沖區(qū)的內(nèi)存量(KB),用于存儲等待寫入磁盤的數(shù)據(jù)。
- cache:用作緩存的內(nèi)存量(KB),用于緩存從磁盤讀取的數(shù)據(jù)。
- si:從磁盤交換進(jìn)內(nèi)存的數(shù)據(jù)量(KB / 秒),如果si和so(從內(nèi)存交換到磁盤的數(shù)據(jù)量)的值長期不為零,表示系統(tǒng)正在頻繁進(jìn)行內(nèi)存交換,這可能會導(dǎo)致系統(tǒng)性能下降,就像頻繁地在倉庫和臨時(shí)存儲點(diǎn)之間搬運(yùn)貨物,效率會降低。
2.2識別內(nèi)存瓶頸與泄漏
- 判斷內(nèi)存瓶頸:當(dāng)系統(tǒng)的內(nèi)存使用率持續(xù)超過 80% ,或者可用內(nèi)存(free命令中的available字段)非常低,接近或低于系統(tǒng)的警戒值時(shí),就可能出現(xiàn)了內(nèi)存瓶頸。此外,如果vmstat命令輸出中的si和so值頻繁變化且較大,說明系統(tǒng)在頻繁地進(jìn)行內(nèi)存和磁盤之間的數(shù)據(jù)交換,這也是內(nèi)存不足的一個重要表現(xiàn)。例如,在一個運(yùn)行著多個服務(wù)的服務(wù)器上,通過監(jiān)控發(fā)現(xiàn)內(nèi)存使用率一直保持在 90% 以上,available內(nèi)存只有幾百 MB ,同時(shí)si和so的值不斷上升,這就表明服務(wù)器可能已經(jīng)出現(xiàn)了內(nèi)存瓶頸,需要進(jìn)一步分析和優(yōu)化。
- 檢測內(nèi)存泄漏:內(nèi)存泄漏是指程序在分配內(nèi)存后,由于某些原因沒有釋放已不再使用的內(nèi)存,導(dǎo)致這些內(nèi)存無法被重新分配和使用,就像倉庫中一些貨架被占用后卻無人清理,越來越多的貨物無處存放。檢測內(nèi)存泄漏可以通過長期監(jiān)控內(nèi)存使用情況來發(fā)現(xiàn)。如果一個進(jìn)程的內(nèi)存使用量持續(xù)增長,即使在其業(yè)務(wù)邏輯沒有明顯變化的情況下也是如此,那么很可能存在內(nèi)存泄漏問題。例如,一個 Web 應(yīng)用程序在運(yùn)行一段時(shí)間后,內(nèi)存占用不斷攀升,最終導(dǎo)致服務(wù)器內(nèi)存耗盡,通過排查發(fā)現(xiàn)是程序中的一個數(shù)據(jù)庫連接池沒有正確釋放連接,導(dǎo)致內(nèi)存泄漏。
內(nèi)存泄漏的危害是非常嚴(yán)重的。它會導(dǎo)致系統(tǒng)可用內(nèi)存逐漸減少,最終耗盡,使系統(tǒng)性能急劇下降,甚至可能導(dǎo)致服務(wù)器崩潰。在一些長期運(yùn)行的服務(wù)中,如數(shù)據(jù)庫服務(wù)器、中間件服務(wù)器等,內(nèi)存泄漏的問題可能會在不知不覺中積累,直到系統(tǒng)出現(xiàn)嚴(yán)重故障才被發(fā)現(xiàn),這會給業(yè)務(wù)帶來極大的影響。所以,及時(shí)發(fā)現(xiàn)并解決內(nèi)存泄漏問題是保障服務(wù)器穩(wěn)定運(yùn)行的關(guān)鍵。
Part3實(shí)戰(zhàn)優(yōu)化策略
找到了內(nèi)存問題,接下來就是要解決它,讓服務(wù)器告別卡頓。下面是一些實(shí)戰(zhàn)優(yōu)化的方法,這些方法都是經(jīng)過實(shí)踐檢驗(yàn)的有效策略。
3.1優(yōu)化內(nèi)存分配策略
在程序開發(fā)中,合理使用malloc()和free()函數(shù)是避免內(nèi)存泄漏和浪費(fèi)的關(guān)鍵。比如,在申請內(nèi)存時(shí),要準(zhǔn)確計(jì)算所需內(nèi)存大小,避免申請過多內(nèi)存造成浪費(fèi)。以一個簡單的字符串處理程序?yàn)槔?,如果要存儲一個長度為n的字符串,應(yīng)該這樣申請內(nèi)存:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int n = 10; // 假設(shè)字符串長度為10
char *str = (char *)malloc((n + 1) * sizeof(char)); // 多申請一個字節(jié)用于存儲字符串結(jié)束符'\0'
if (str == NULL) {
perror("malloc failed");
return 1;
}
strcpy(str, "example");
// 使用字符串
printf("String: %s\n", str);
free(str); // 使用完后釋放內(nèi)存
str = NULL; // 將指針置為NULL,防止野指針
return 0;
}在這個例子中,我們先使用malloc函數(shù)申請了足夠的內(nèi)存空間來存儲字符串及其結(jié)束符,并且在使用完后及時(shí)調(diào)用free函數(shù)釋放內(nèi)存,并將指針置為NULL,這樣可以有效避免內(nèi)存泄漏。
除了正確使用malloc()和free()函數(shù),還可以借助一些內(nèi)存管理工具來優(yōu)化內(nèi)存分配。比如memon工具,它可以實(shí)時(shí)監(jiān)控進(jìn)程的內(nèi)存使用情況,幫助我們發(fā)現(xiàn)內(nèi)存使用異常的地方。memon工具提供了詳細(xì)的內(nèi)存使用統(tǒng)計(jì)信息,包括內(nèi)存分配次數(shù)、釋放次數(shù)、當(dāng)前內(nèi)存使用量等。通過這些信息,我們可以深入了解程序的內(nèi)存使用模式,找出可能存在的內(nèi)存浪費(fèi)或泄漏點(diǎn)。例如,我們可以通過memon觀察到某個函數(shù)中頻繁地進(jìn)行小內(nèi)存塊的分配和釋放,這可能導(dǎo)致內(nèi)存碎片的產(chǎn)生,進(jìn)而影響程序性能,此時(shí)就可以考慮優(yōu)化內(nèi)存分配策略,如使用內(nèi)存池技術(shù)來減少內(nèi)存分配次數(shù)。
slabtop工具則用于監(jiān)控內(nèi)核的slab緩存,slab緩存是內(nèi)核用于管理小對象的內(nèi)存分配機(jī)制,通過slabtop可以查看各個slab緩存的使用情況,包括對象數(shù)量、緩存大小等 。如果發(fā)現(xiàn)某個slab緩存的對象數(shù)量過多或緩存大小不合理,可以進(jìn)一步分析原因并進(jìn)行優(yōu)化。比如,如果某個slab緩存中對象的創(chuàng)建和銷毀非常頻繁,可能需要調(diào)整緩存的大小或優(yōu)化對象的生命周期管理,以提高內(nèi)存使用效率。例如,在一個網(wǎng)絡(luò)服務(wù)器程序中,通過slabtop發(fā)現(xiàn)socket相關(guān)的slab緩存占用了大量內(nèi)存,進(jìn)一步分析發(fā)現(xiàn)是由于短連接的頻繁創(chuàng)建和銷毀導(dǎo)致的,通過優(yōu)化連接管理策略,減少了不必要的短連接,從而降低了slab緩存的內(nèi)存占用。
3.2巧用交換空間
交換空間(Swap Space)是磁盤上的一塊區(qū)域,當(dāng)物理內(nèi)存不足時(shí),系統(tǒng)會將內(nèi)存中不常用的數(shù)據(jù)轉(zhuǎn)移到交換空間中,從而釋放物理內(nèi)存供其他程序使用 。交換空間就像是一個臨時(shí)的內(nèi)存?zhèn)}庫,在物理內(nèi)存緊張時(shí)發(fā)揮作用。
設(shè)置交換空間的方法有兩種,一種是使用分區(qū),另一種是使用文件。以使用文件設(shè)置交換空間為例,首先使用dd命令創(chuàng)建一個指定大小的文件,比如創(chuàng)建一個 2GB 的交換文件:
sudo dd if=/dev/zero of=/swapfile bs=1G count=2這里if=/dev/zero表示從/dev/zero設(shè)備讀取數(shù)據(jù),/dev/zero是一個特殊文件,它會不斷輸出空字符(\0);of=/swapfile表示將數(shù)據(jù)輸出到/swapfile文件中;bs=1G表示每次讀寫的塊大小為 1GB;count=2表示讀取 2 次,即創(chuàng)建一個 2GB 大小的文件。
創(chuàng)建好文件后,需要將其格式化為交換文件:
sudo mkswap /swapfile然后啟用交換文件:
sudo swapon /swapfile為了使交換文件在系統(tǒng)重啟后仍然生效,還需要將其添加到/etc/fstab文件中,在/etc/fstab文件中添加一行:
/swapfile swap swap defaults 0 0交換空間的大小并非越大越好,需要根據(jù)系統(tǒng)的實(shí)際情況進(jìn)行調(diào)整。如果交換空間設(shè)置過小,當(dāng)物理內(nèi)存不足時(shí),系統(tǒng)可能無法及時(shí)將數(shù)據(jù)轉(zhuǎn)移到交換空間,導(dǎo)致系統(tǒng)內(nèi)存耗盡,出現(xiàn)卡頓甚至崩潰;而如果交換空間設(shè)置過大,會占用過多的磁盤空間,并且在使用交換空間時(shí),由于磁盤讀寫速度遠(yuǎn)低于內(nèi)存讀寫速度,會導(dǎo)致系統(tǒng)性能下降 。一般來說,可以根據(jù)物理內(nèi)存的大小來初步確定交換空間的大小。對于物理內(nèi)存較小(如小于 2GB)的系統(tǒng),交換空間可以設(shè)置為物理內(nèi)存的 2 倍左右;對于物理內(nèi)存較大(如大于 8GB)的系統(tǒng),交換空間可以設(shè)置為 2GB - 4GB 。
但這只是一個參考值,實(shí)際設(shè)置時(shí)還需要結(jié)合系統(tǒng)的負(fù)載情況、應(yīng)用程序的內(nèi)存使用特點(diǎn)等因素進(jìn)行調(diào)整。例如,對于一個運(yùn)行著數(shù)據(jù)庫服務(wù)器的系統(tǒng),由于數(shù)據(jù)庫對內(nèi)存的讀寫性能要求較高,應(yīng)盡量減少對交換空間的依賴,此時(shí)交換空間可以設(shè)置得相對較??;而對于一個桌面系統(tǒng),在物理內(nèi)存不足時(shí),可以適當(dāng)增大交換空間來保證系統(tǒng)的基本運(yùn)行。
3.3調(diào)整關(guān)鍵內(nèi)核參數(shù)
Linux 內(nèi)核提供了一些參數(shù),可以用于調(diào)整系統(tǒng)的內(nèi)存管理策略。通過適當(dāng)調(diào)整這些參數(shù),可以改變系統(tǒng)的內(nèi)存分配行為,從而提高系統(tǒng)的性能 。
vm.swappiness參數(shù)控制了內(nèi)核對于交換空間與物理內(nèi)存的使用程度,其取值范圍是 0 - 100 。0 表示盡可能少使用交換空間,即只有在物理內(nèi)存完全耗盡時(shí)才會使用交換空間;100 表示盡可能多地使用交換空間 。對于內(nèi)存較小的系統(tǒng),可以將vm.swappiness的值調(diào)低,比如設(shè)置為 10,以減少對交換空間的使用,提升系統(tǒng)性能 。可以通過修改/etc/sysctl.conf文件來設(shè)置vm.swappiness參數(shù),在文件中添加或修改一行:
vm.swappiness = 10然后執(zhí)行sudo sysctl -p使設(shè)置生效。
vm.dirty_ratio表示內(nèi)存中臟數(shù)據(jù)的比例上限,當(dāng)達(dá)到這個比例時(shí)系統(tǒng)將啟動寫入磁盤的操作 。臟數(shù)據(jù)是指已經(jīng)被修改但還未寫入磁盤的數(shù)據(jù)。vm.dirty_background_ratio表示后臺寫入的臟數(shù)據(jù)比例上限 。當(dāng)臟數(shù)據(jù)達(dá)到vm.dirty_background_ratio時(shí),系統(tǒng)會在后臺啟動線程將臟數(shù)據(jù)寫入磁盤,而當(dāng)臟數(shù)據(jù)達(dá)到vm.dirty_ratio時(shí),系統(tǒng)會主動阻塞應(yīng)用程序的寫操作,直到臟數(shù)據(jù)被寫入磁盤 。合理調(diào)整這兩個參數(shù)可以平衡系統(tǒng)的性能與數(shù)據(jù)保護(hù)的需求。
例如,對于一個 I/O 負(fù)載較高的系統(tǒng),可以適當(dāng)降低vm.dirty_ratio和vm.dirty_background_ratio的值,比如將vm.dirty_ratio設(shè)置為 30,vm.dirty_background_ratio設(shè)置為 10,這樣可以減少臟數(shù)據(jù)在內(nèi)存中的積累,避免 I/O 操作過于集中,從而提高系統(tǒng)的穩(wěn)定性。同樣,在/etc/sysctl.conf文件中添加或修改這兩個參數(shù)的值:
vm.dirty_ratio = 30
vm.dirty_background_ratio = 10然后執(zhí)行sudo sysctl -p使設(shè)置生效。
3.4清理緩存與臨時(shí)文件
在 Linux 系統(tǒng)運(yùn)行過程中,內(nèi)存緩存會占用一定的內(nèi)存空間,臨時(shí)文件也會占用磁盤空間,定期清理這些緩存和臨時(shí)文件可以釋放資源,提高系統(tǒng)性能。
清理內(nèi)存緩存可以使用以下命令:
sudo sync
sudo echo 3 > /proc/sys/vm/drop_cachessync命令用于將緩存中的數(shù)據(jù)同步到磁盤,確保數(shù)據(jù)的完整性 。echo 3 > /proc/sys/vm/drop_caches表示清理內(nèi)存緩存,其中3表示清理 pagecache、dentries 和 inodes 。pagecache 是文件系統(tǒng)的頁面緩存,用于加速文件的讀寫操作;dentries 是目錄項(xiàng)緩存,用于緩存目錄的元數(shù)據(jù);inodes 是索引節(jié)點(diǎn)緩存,用于緩存文件的元數(shù)據(jù) 。通過清理這些緩存,可以釋放內(nèi)存空間,讓系統(tǒng)有更多的內(nèi)存可供其他程序使用。
臨時(shí)文件通常存放在/tmp目錄下,定期清理/tmp目錄下的臨時(shí)文件可以釋放磁盤空間 ??梢允褂靡韵旅钋謇?tmp目錄下超過 7 天的文件:
sudo find /tmp -type f -mtime +7 -exec rm -f {} \;這里find /tmp -type f -mtime +7表示在/tmp目錄下查找類型為文件且修改時(shí)間超過 7 天的文件,-exec rm -f {} \;表示對找到的文件執(zhí)行刪除操作 。此外,還可以定期清理系統(tǒng)日志文件,日志文件通常存放在/var/log目錄下,隨著時(shí)間的推移,日志文件會不斷增大,占用大量磁盤空間 ??梢愿鶕?jù)實(shí)際情況,定期刪除一些舊的日志文件,或者對日志文件進(jìn)行壓縮歸檔 。例如,對于/var/log/syslog文件,可以每個月進(jìn)行一次壓縮歸檔,命令如下:
sudo mv /var/log/syslog /var/log/syslog.`date +%Y%m`
sudo gzip /var/log/syslog.`date +%Y%m`
sudo touch /var/log/syslog
sudo chown syslog:adm /var/log/syslog
sudo chmod 640 /var/log/syslog這樣就將當(dāng)前的syslog文件重命名為帶有年月的文件,并進(jìn)行壓縮,然后創(chuàng)建一個新的syslog文件,保證系統(tǒng)日志的正常記錄。
3.5優(yōu)化應(yīng)用程序內(nèi)存使用
應(yīng)用程序的內(nèi)存使用情況對服務(wù)器性能有著重要影響,我們可以借助一些工具來分析和優(yōu)化應(yīng)用程序的內(nèi)存使用。
valgrind是一個功能強(qiáng)大的內(nèi)存分析工具,主要用于內(nèi)存泄漏檢測、內(nèi)存訪問錯誤和性能分析 。它包含多個工具,如Memcheck用于檢測內(nèi)存錯誤,如內(nèi)存泄漏、非法內(nèi)存訪問等;Callgrind用于收集程序運(yùn)行時(shí)的函數(shù)調(diào)用信息,幫助進(jìn)行性能分析 。以檢測內(nèi)存泄漏為例,使用valgrind的Memcheck工具來檢測一個簡單的 C 程序的內(nèi)存泄漏:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(10 * sizeof(int));
if (ptr == NULL) {
perror("malloc failed");
return 1;
}
// 這里忘記釋放ptr指向的內(nèi)存
return 0;
}編譯該程序:
gcc -g -o test test.c然后使用valgrind檢測:
valgrind --tool=memcheck./testvalgrind會輸出詳細(xì)的內(nèi)存錯誤信息,包括內(nèi)存泄漏的位置和大小等,通過這些信息我們可以定位并修復(fù)內(nèi)存泄漏問題 。在上述例子中,valgrind會提示ptr指向的內(nèi)存未被釋放,我們可以在程序結(jié)束前添加free(ptr);來釋放內(nèi)存。
perf是 Linux 系統(tǒng)自帶的性能分析工具,它可以用于分析應(yīng)用程序的性能瓶頸,包括內(nèi)存使用方面的問題 。通過perf可以查看應(yīng)用程序中各個函數(shù)的執(zhí)行時(shí)間、CPU 占用率以及內(nèi)存訪問情況等 。例如,使用perf來分析一個程序的內(nèi)存訪問情況:
perf record -g -e mem:all_loads,mem:all_stores -a -o perf.data這里-g表示記錄調(diào)用關(guān)系,-e mem:all_loads,mem:all_stores表示記錄所有的內(nèi)存加載和存儲事件,-a表示針對所有 CPU,-o perf.data表示將結(jié)果輸出到perf.data文件中 。然后使用perf report命令來查看分析結(jié)果:
perf report -i perf.data通過perf report的輸出,我們可以看到哪些函數(shù)的內(nèi)存訪問次數(shù)較多,哪些內(nèi)存訪問操作花費(fèi)的時(shí)間較長,從而有針對性地優(yōu)化應(yīng)用程序的內(nèi)存使用 。比如,如果發(fā)現(xiàn)某個函數(shù)中存在大量的不必要的內(nèi)存讀寫操作,可以對該函數(shù)進(jìn)行優(yōu)化,減少內(nèi)存訪問次數(shù),提高程序的執(zhí)行效率。
Part4內(nèi)存調(diào)優(yōu)工具
面對內(nèi)存問題的重重挑戰(zhàn),一系列強(qiáng)大的內(nèi)存優(yōu)化調(diào)優(yōu)工具應(yīng)運(yùn)而生,它們就像訓(xùn)練有素的 “特種兵”,各自具備獨(dú)特的技能,能夠精準(zhǔn)地解決各種內(nèi)存難題。接下來,讓我們深入了解這些工具的神奇之處。
4.1 jstat:內(nèi)存數(shù)據(jù)的 “偵察兵”
jstat(Java Virtual Machine Statistics Monitoring Tool)是 JDK 自帶的命令行工具 ,堪稱內(nèi)存數(shù)據(jù)的 “偵察兵”,主要用于監(jiān)控 JVM 運(yùn)行時(shí)的狀態(tài)信息,包括類加載、垃圾回收、堆內(nèi)存、編譯等方面的統(tǒng)計(jì)數(shù)據(jù)。它無需圖形界面,特別適合在服務(wù)端進(jìn)行性能監(jiān)控與調(diào)優(yōu)工作。
jstat 的使用方式非常簡單,在命令行中輸入 “jstat [options] [interval] [count]” 即可。其中,“options” 是監(jiān)控選項(xiàng),比如 “-gc” 用于輸出垃圾回收的相關(guān)信息,“-gcutil” 則以百分比形式展示垃圾回收的利用率;“vmid” 是 JVM 進(jìn)程 ID,可以通過 jps 命令輕松獲??;“interval” 表示采樣間隔時(shí)間,單位為毫秒;“count” 是采樣次數(shù),如果不設(shè)置,默認(rèn)會持續(xù)輸出。
舉個例子,當(dāng)我們想要監(jiān)控某個 Java 程序(假設(shè)其進(jìn)程 ID 為 12345)的垃圾回收情況,每秒采樣一次,共采樣 5 次,就可以使用命令 “jstat -gcutil 12345 1000 5”。執(zhí)行后,會得到類似如下的輸出:
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 50.07 5.38 85.30 95.80 80.50 200 10.205 3.50 13.70這些數(shù)據(jù)分別代表:S0 和 S1 是幸存區(qū)的使用率;E 是 Eden 區(qū)的使用率;O 是老年代的使用率;M 是元空間的使用率;CCS 是壓縮類空間的使用率;YGC 是年輕代垃圾回收的次數(shù);YGCT 是年輕代垃圾回收的總耗時(shí);FGC 是 Full GC 的次數(shù);FGCT 是 Full GC 的總耗時(shí);GCT 是垃圾回收的總耗時(shí)。通過分析這些數(shù)據(jù),我們可以了解到 Java 程序的內(nèi)存使用趨勢,判斷是否存在內(nèi)存泄漏或垃圾回收效率低下等問題。比如,如果發(fā)現(xiàn)老年代(O)的使用率持續(xù)升高,接近容量上限,且 Full GC 頻繁發(fā)生,就可能存在內(nèi)存泄漏,需要進(jìn)一步排查。
4.2 jmap:內(nèi)存鏡像的 “雕刻師”
jmap(Java Memory Map)是一個可以深入了解 Java 進(jìn)程內(nèi)存使用情況的工具,它如同一位 “雕刻師”,能夠生成內(nèi)存快照,幫助我們分析內(nèi)存泄漏和對象分布。其功能十分強(qiáng)大,可以輸出所有內(nèi)存中對象的詳細(xì)信息,甚至能將 VM 中的 heap 以二進(jìn)制形式輸出成文本。
在排查 Java 程序內(nèi)存泄漏問題時(shí),jmap 發(fā)揮著重要作用。假設(shè)我們懷疑某個 Java 進(jìn)程(進(jìn)程 ID 為 3024)存在內(nèi)存泄漏,首先可以使用 “jmap -histo 3024” 命令,它會打印出該進(jìn)程內(nèi)存內(nèi)所有對象的情況,包括產(chǎn)生了哪些對象及其數(shù)量。如果想將這些信息保存到文件中以便后續(xù)分析,在 SHELL 中可采用 “jmap -histo 3024 > a.log” 的方式。
若要更深入地分析,還可以使用 “jmap -dump:format=b,file=outfile 3024” 命令,將 3024 進(jìn)程的內(nèi)存 heap 輸出到 outfile 文件里。這個文件就像是內(nèi)存的一個 “快照”,記錄了特定時(shí)刻內(nèi)存中對象的狀態(tài)和分布。之后,我們可以配合強(qiáng)大的內(nèi)存分析工具 MAT(Memory Analyzer Tool)對這個快照文件進(jìn)行深入剖析,找出內(nèi)存泄漏的根源。
另外,使用 “jmap -heap 3024” 命令,能夠查看進(jìn)程堆內(nèi)存的使用情況,包括所使用的 GC 算法、堆配置參數(shù)以及各代中堆內(nèi)存的使用情況。例如,通過分析這些信息,我們可以判斷堆內(nèi)存的分配是否合理,是否需要調(diào)整堆的大小或 GC 策略來優(yōu)化內(nèi)存使用 。
4.3 VisualVM:可視化監(jiān)控 “大師”
VisualVM 是一款功能強(qiáng)大的可視化監(jiān)控工具,它為開發(fā)者提供了直觀的界面,就像一位 “可視化監(jiān)控大師”,可以實(shí)時(shí)監(jiān)控 Java 應(yīng)用程序的內(nèi)存、CPU、線程等關(guān)鍵指標(biāo),幫助我們?nèi)媪私鈶?yīng)用程序的運(yùn)行狀態(tài),進(jìn)而優(yōu)化 Java 應(yīng)用性能。
在使用 VisualVM 優(yōu)化 Java 應(yīng)用性能時(shí),其操作簡單便捷。當(dāng)我們啟動 Java VisualVM 后,會在界面左側(cè)看到運(yùn)行中的 Java 進(jìn)程列表。點(diǎn)擊想要分析的應(yīng)用程序,右鍵選擇 “監(jiān)控”,即可進(jìn)入監(jiān)控界面。在這里,我們能清晰地看到各種指標(biāo)的實(shí)時(shí)數(shù)據(jù)和變化趨勢。
比如,在 CPU 性能監(jiān)控方面,它會以圖表的形式展示程序在運(yùn)行時(shí)的 CPU 使用率,讓我們直觀地了解到程序?qū)?CPU 資源的占用情況。通過觀察 CPU 使用率的峰值和持續(xù)時(shí)間,我們可以判斷程序中是否存在某些高計(jì)算量的代碼塊,從而針對性地進(jìn)行優(yōu)化。
在內(nèi)存使用情況監(jiān)控上,VisualVM 同樣以直觀的圖表呈現(xiàn)程序使用的內(nèi)存量,包括堆內(nèi)存和非堆內(nèi)存的使用情況。如果發(fā)現(xiàn)內(nèi)存使用量持續(xù)上升且沒有明顯的回落,可能意味著存在內(nèi)存泄漏問題。此時(shí),我們可以利用 VisualVM 的 “Heap Dump” 功能生成堆轉(zhuǎn)儲文件,進(jìn)一步分析對象的引用鏈,找出導(dǎo)致內(nèi)存泄漏的對象。
此外,VisualVM 還支持安裝各種插件,以擴(kuò)展其功能。例如,安裝 GC 監(jiān)控插件后,可以更詳細(xì)地了解垃圾回收的過程和性能指標(biāo);安裝線程分析插件,則能深入分析線程的狀態(tài)和活動,排查線程死鎖等問題。通過這些豐富的功能和插件支持,VisualVM 成為了 Java 開發(fā)者優(yōu)化應(yīng)用性能的得力助手。
4.4 MAT:內(nèi)存分析的 “福爾摩斯”
MAT(Memory Analyzer Tool)是一款專門用于深入分析 Java 堆內(nèi)存的工具,它就像一位敏銳的 “福爾摩斯”,能夠?qū)?nèi)存快照進(jìn)行細(xì)致入微的分析,幫助我們找出內(nèi)存泄漏的原因和大對象,解決復(fù)雜的內(nèi)存問題。
當(dāng)面對復(fù)雜的內(nèi)存問題時(shí),MAT 的強(qiáng)大分析能力就得以充分展現(xiàn)。假設(shè)我們已經(jīng)使用 jmap 工具生成了堆轉(zhuǎn)儲文件(例如 heapdump.hprof),接下來就可以使用 MAT 打開這個文件進(jìn)行分析。
MAT 提供了多種分析功能和工具。其中,“Leak Suspects Report” 是一個非常實(shí)用的功能,它會自動分析堆轉(zhuǎn)儲文件,生成報(bào)告,幫助我們快速識別可能的內(nèi)存泄漏源。報(bào)告中會列出疑似內(nèi)存泄漏的對象,并給出相關(guān)的分析和建議。
在分析內(nèi)存快照時(shí),MAT 還能展示對象之間的引用關(guān)系,讓我們清晰地看到哪些對象被哪些其他對象所引用,從而找出長生命周期對象持有短生命周期對象引用的情況,這往往是導(dǎo)致內(nèi)存泄漏的常見原因之一。同時(shí),通過查看對象的 Retained Heap(保留堆)大小,我們可以確定哪些對象占用了大量內(nèi)存,進(jìn)而對這些大對象進(jìn)行深入分析,判斷它們是否合理存在,是否需要進(jìn)行優(yōu)化或清理。
另外,MAT 還支持使用 OQL(Object Query Language)進(jìn)行自定義查詢,這為我們深入分析內(nèi)存數(shù)據(jù)提供了更大的靈活性。例如,我們可以通過 OQL 查詢特定類型的對象、按照對象的屬性進(jìn)行篩選等,以便更精準(zhǔn)地定位內(nèi)存問題 。


























