金融業(yè)反洗錢培訓(xùn)網(wǎng)站seo咨詢推廣
目錄
1、WebRTC簡介
2、問題現(xiàn)象描述
3、將Windbg附加到目標(biāo)進(jìn)程上分析
3.1、Windbg沒有附加到主程序進(jìn)程上,沒有感知到異?;蛑袛?/p>
3.2、Windbg感知到了中斷,中斷在DebugBreak函數(shù)調(diào)用上?? ?
3.3、32位進(jìn)程用戶態(tài)虛擬地址和內(nèi)核態(tài)虛擬地址的劃分
4、用戶態(tài)內(nèi)存不足問題分析虛擬
4.1、判斷是內(nèi)存不足導(dǎo)致了malloc申請(qǐng)內(nèi)存失敗
4.2、為啥會(huì)中斷在DebugBreak函數(shù)調(diào)用處呢?
5、占用程序進(jìn)程的虛擬內(nèi)存的因素有哪些??? ?
5.1、二進(jìn)制文件
5.2、線程的??臻g
5.3、程序中申請(qǐng)的堆內(nèi)存
6、當(dāng)前用戶態(tài)虛擬內(nèi)存占用高的解決辦法
6.1、修改WebRTC編譯選項(xiàng),減少內(nèi)存占用
6.2、將程序做成64位的
6.3、使用Visual Studio的鏈接選項(xiàng),將用戶態(tài)虛擬內(nèi)存從2GB擴(kuò)充到3GB
6.4、使用多進(jìn)程模式
7、最后
VC++常用功能開發(fā)匯總(專欄文章列表,歡迎訂閱,持續(xù)更新...)https://blog.csdn.net/chenlycly/article/details/124272585C++軟件異常排查從入門到精通系列教程(專欄文章列表,歡迎訂閱,持續(xù)更新...)
https://blog.csdn.net/chenlycly/article/details/125529931C++軟件分析工具從入門到精通案例集錦(專欄文章正在更新中...)
https://blog.csdn.net/chenlycly/article/details/131405795C/C++基礎(chǔ)與進(jìn)階(專欄文章,持續(xù)更新中...)
https://blog.csdn.net/chenlycly/category_11931267.html開源組件及數(shù)據(jù)庫技術(shù)(專欄文章,持續(xù)更新中...)
https://blog.csdn.net/chenlycly/category_12458859.html? ? ? ?本文分享一下最近遇到的一個(gè)因虛擬內(nèi)存不足導(dǎo)致程序發(fā)生閃退的問題排查案例,案例具有一定的代表性,希望能給大家提供一定的借鑒或參考。
1、WebRTC簡介
? ? ? ?本案例中的軟件是基于開源庫WebRTC構(gòu)建的,發(fā)生的軟件問題也是與WebRTC有關(guān)的,所以先給大家簡要地介紹一下WebRTC相關(guān)的內(nèi)容。
? ? ? ?WebRTC(Web Real-Time Communication)是一個(gè)由Google發(fā)起的實(shí)時(shí)音視頻通訊C++開源庫,其提供了音視頻采集、編碼、網(wǎng)絡(luò)傳輸,解碼顯示等一整套音視頻解決方案,我們可以通過該開源庫快速地構(gòu)建出一個(gè)音視頻通訊應(yīng)用。
一個(gè)實(shí)時(shí)音視頻應(yīng)用軟件一般都會(huì)包括這樣幾個(gè)環(huán)節(jié):音視頻采集、音視頻編碼(壓縮)、前后處理(美顏、濾鏡、回聲消除、噪聲抑制等)、網(wǎng)絡(luò)傳輸、解碼渲染(音視頻播放)等。其中每一個(gè)細(xì)分環(huán)節(jié),還有更細(xì)分的技術(shù)模塊。
? ? ? 雖然其名為WebRTC,但是實(shí)際上它不光支持Web之間的音視頻通訊,還支持Windows、Android以及iOS等移動(dòng)平臺(tái)。WebRTC底層是用C/C++開發(fā)的,具有良好的跨平臺(tái)性能。
? ? ? ?WebRTC因?yàn)槠漭^好的音視頻效果及良好的網(wǎng)絡(luò)適應(yīng)性,目前已被廣泛的應(yīng)用到視頻會(huì)議、實(shí)時(shí)音視頻直播等領(lǐng)域中。在視頻會(huì)議領(lǐng)域,騰訊會(huì)議、華為WeLink、字節(jié)飛書、阿里釘釘、小魚易連、廈門億聯(lián)等國產(chǎn)廠商均提供了基于WebRTC方案的視頻會(huì)議。
? ? ? ?大家熟知的音視頻專業(yè)服務(wù)商聲網(wǎng)(Agora),更是基于開源WebRTC庫,提供了社交直播、教育、游戲電競、IoT、AR/VR、金融、保險(xiǎn)、醫(yī)療、企業(yè)協(xié)作等多個(gè)行業(yè)的音視頻互動(dòng)解決方案。使用聲網(wǎng)服務(wù)的企業(yè)包括小米、陌陌、斗魚、嗶哩嗶哩、新東方、小紅書、HTC VIVE 、The Meet Group、Bunch、Yalla等遍布全球的巨頭、獨(dú)角獸及創(chuàng)業(yè)企業(yè)。除了頭部公司聲網(wǎng)之外,也陸續(xù)有多家公司基于開源的WebRTC,開發(fā)出了多個(gè)音視頻應(yīng)用,提供了多個(gè)領(lǐng)域的音視頻通信解決方案。
2、問題現(xiàn)象描述
? ? ? ?基于WebRTC的會(huì)議軟件在加入會(huì)議后,會(huì)時(shí)不時(shí)發(fā)生閃退,雖然不是必現(xiàn)的,但已經(jīng)有好幾個(gè)用戶反饋了,并且在某技術(shù)支持同事的電腦上也出現(xiàn)了頻繁閃退的問題。程序閃退時(shí),程序中安裝的異常捕獲模塊沒有感知到,所以沒有生成dump文件。
雖然我們?cè)诔绦蛑邪惭b了異常捕獲模塊,但并不能捕獲所有的軟件異?;虮罎?#xff0c;只能捕獲大部分的異常,所以程序發(fā)生異常時(shí)沒有感知到,也實(shí)屬正常。
3、將Windbg附加到目標(biāo)進(jìn)程上分析
? ? ? ?對(duì)于這類沒有生成dump文件的場景,就需要使用Windbg進(jìn)行動(dòng)態(tài)調(diào)試了,即將Windbg附加到目標(biāo)進(jìn)程上,和目標(biāo)進(jìn)程一起跑。于是讓這個(gè)技術(shù)支持同事每次運(yùn)行軟件時(shí),都手動(dòng)將Windbg附加到進(jìn)程上,和進(jìn)程一起跑,這樣一旦程序發(fā)生異常,Windbg就會(huì)感知到,就會(huì)中斷下來,這樣就能看到出問題時(shí)的函數(shù)調(diào)用堆棧,就可以分析了。
3.1、Windbg沒有附加到主程序進(jìn)程上,沒有感知到異?;蛑袛?/h4>
? ? ? ?但這位同事復(fù)現(xiàn)問題后,Windbg并沒有中斷下來,即Windbg沒有感知到,按講是不應(yīng)該的,一旦程序發(fā)生異常,正在調(diào)試的Windbg會(huì)第一時(shí)間感知到并中斷下來。后來發(fā)現(xiàn),他是在Windbg中打開桌面快捷方式文件去啟動(dòng)軟件的,而桌面快捷方式指向的是個(gè)引導(dǎo)啟動(dòng)的程序,不是主程序,該程序做一些初始校驗(yàn)的操作,校驗(yàn)通過后會(huì)自動(dòng)將主程序啟動(dòng)起來。這樣Windbg附加到的是引導(dǎo)程序的進(jìn)程上,并沒有附加到主程序進(jìn)程上。
就像QQ的桌面快捷方式指向的是QQ安裝目錄下的QQScLauncher.exe:
該程序會(huì)做一些啟動(dòng)主程序QQ.exe前的一些檢查,然后會(huì)將主程序QQ.exe啟動(dòng)起來。
在我們這個(gè)問題中,可以先將主程序啟動(dòng)起來,然后再將Widnbg附加到主程序進(jìn)程上,就可以了。也可以通過Windbg將主程序啟動(dòng)起來,需要到安裝目錄中找到主程序的exe文件,打開該文件,不能通過桌面快捷方式去啟動(dòng)。
3.2、Windbg感知到了中斷,中斷在DebugBreak函數(shù)調(diào)用上?? ?
? ? ? ?后來同事每次運(yùn)行程序時(shí)都將Windbg附加到程序進(jìn)程上,復(fù)現(xiàn)了問題,正在調(diào)試的Windbg中斷了下來,發(fā)現(xiàn)中斷在DebugBreak接口調(diào)用處,如下所示:
輸入kn命令查看此時(shí)的函數(shù)調(diào)用堆棧:?
正是DebugBreak接口就是讓正在調(diào)試的進(jìn)程中斷下來的。使用kn命令查看此時(shí)的函數(shù)調(diào)用堆棧,發(fā)現(xiàn)是WebRTC庫中在調(diào)用malloc動(dòng)態(tài)申請(qǐng)內(nèi)存時(shí)返回了NULL,然后WebRTC庫內(nèi)部認(rèn)為是異常情況,可能是認(rèn)為內(nèi)存申請(qǐng)不到后相關(guān)的業(yè)務(wù)都沒法執(zhí)行了,程序繼續(xù)運(yùn)行就沒有意義了,于是直接調(diào)用abort接口將進(jìn)程終止了。
? ? ? ?這就能說明為啥程序閃退時(shí)異常捕獲模塊沒有感知到異常,因?yàn)閙alloc申請(qǐng)內(nèi)存失敗返回NULL,并沒有產(chǎn)生C++異常,程序閃退是因?yàn)閃ebRTC內(nèi)部調(diào)用abort接口強(qiáng)行將進(jìn)程終止掉了。
? ? ? ?至于為啥會(huì)出現(xiàn)malloc申請(qǐng)內(nèi)存失敗的問題呢?估計(jì)是用戶態(tài)的虛擬內(nèi)存不夠用了,我們的程序是32位的,系統(tǒng)會(huì)給程序進(jìn)程分配4GB的虛擬內(nèi)存,其中2GB是用戶態(tài)的虛擬內(nèi)存,我們的程序用戶態(tài)的內(nèi)存快到達(dá)上限了,沒有空閑內(nèi)存可用了,所以malloc申請(qǐng)內(nèi)存失敗了,返回了NULL。
? ? ? 關(guān)于如何使用Windbg進(jìn)行動(dòng)態(tài)調(diào)試(使用Windbg進(jìn)行動(dòng)態(tài)調(diào)試的完整步驟),可以參見我之前寫的文章:
使用Windbg動(dòng)態(tài)調(diào)試目標(biāo)進(jìn)程的一般步驟及要點(diǎn)詳解https://blog.csdn.net/chenlycly/article/details/131029795
3.3、32位進(jìn)程用戶態(tài)虛擬地址和內(nèi)核態(tài)虛擬地址的劃分
? ? ? ?對(duì)于32為程序,是按32位進(jìn)行內(nèi)存尋址的,所以給32位程序進(jìn)程分配了4GB的虛擬內(nèi)存,程序中所使用的內(nèi)存均是從這4GB的虛擬內(nèi)存上劃撥的,比如全局變量占用的內(nèi)存、線程棧內(nèi)存、程序申請(qǐng)的堆內(nèi)存、二進(jìn)制文件占用的代碼段內(nèi)存等。
? ? ? ?32位進(jìn)程的這4GB虛擬內(nèi)存,在Windows平臺(tái)上,默認(rèn)情況下,2GB是用戶態(tài)虛擬內(nèi)存,2GB是內(nèi)核態(tài)虛擬內(nèi)存;在Linux平臺(tái)上,默認(rèn)情況下,3GB是用戶態(tài)虛擬內(nèi)存,1GB是內(nèi)核態(tài)虛擬內(nèi)存。
? ? ? ?此外,用戶態(tài)的代碼只能訪問用戶態(tài)的內(nèi)存地址,是禁止訪問內(nèi)核態(tài)內(nèi)存地址的;內(nèi)核態(tài)的代碼只能訪問內(nèi)核態(tài)的內(nèi)存地址,是禁止訪問用戶態(tài)內(nèi)存地址的。關(guān)于32位進(jìn)程和64位進(jìn)程的虛擬內(nèi)存地址劃分,可以參見《Windows核心編程》一書中的截圖:
? ? ? ?我們以前在排查軟件異常崩潰時(shí),經(jīng)常遇到崩潰的那條匯編代碼(位于用戶態(tài)代碼中)中訪問了內(nèi)核態(tài)的內(nèi)存地址(可能是訪問了未初始化的變量內(nèi)存,也可能是訪問了因內(nèi)存越界被篡改的內(nèi)存),用戶態(tài)代碼是禁止訪問內(nèi)核態(tài)內(nèi)存地址的,強(qiáng)行訪問會(huì)觸發(fā)內(nèi)存訪問違例,引發(fā)程序崩潰。
? ? ? C++軟件異?;緝?nèi)存有關(guān),關(guān)于引發(fā)C++程序內(nèi)存錯(cuò)誤的常見原因,可以參見我之前的文章:
引發(fā)C++程序內(nèi)存錯(cuò)誤的常見原因分析與總結(jié)https://blog.csdn.net/chenlycly/article/details/128599525
4、用戶態(tài)內(nèi)存不足問題分析虛擬
4.1、判斷是內(nèi)存不足導(dǎo)致了malloc申請(qǐng)內(nèi)存失敗
? ? ? ?復(fù)現(xiàn)問題時(shí)Windbg正好中斷下來,軟件進(jìn)程暫停下來,正好此時(shí)使用Process Explorer查看我們軟件進(jìn)程的虛擬內(nèi)存,確實(shí)已經(jīng)用到1.8GB左右了,快接近2GB的上限了!其實(shí)離2GB的上限還有200MB的空余,但可能因?yàn)閮?nèi)存碎片的存在,都是一小塊一小塊分散的小內(nèi)存塊,而程序中要申請(qǐng)的是一段連續(xù)的內(nèi)存塊,找不到指定大小的連續(xù)內(nèi)存塊,就會(huì)出現(xiàn)內(nèi)存分配失敗了。
? ? ? ?如果在正在調(diào)試的Windbg中使用.dump命令手動(dòng)導(dǎo)出dump文件,我們也可以事后通過dump文件的大小去初步估計(jì)出問題時(shí)進(jìn)程占用的虛擬內(nèi)存大小。在Windbg中導(dǎo)出的dump文件,屬于全dump文件,將當(dāng)時(shí)進(jìn)程的內(nèi)存信息都保存了下來,dump文件的大小接近當(dāng)時(shí)進(jìn)程的虛擬內(nèi)存大小,可能會(huì)略小一點(diǎn)點(diǎn)。
? ? ? ?內(nèi)存碎片的概念之前聽說過,在這種場景下才感受到內(nèi)存碎片的危害!有人說可以使用內(nèi)存池,內(nèi)存池可以減少內(nèi)存碎片的出現(xiàn),但實(shí)際上程序業(yè)務(wù)需要占用更多的內(nèi)存,減少內(nèi)存碎片也解決不了問題。
4.2、為啥會(huì)中斷在DebugBreak函數(shù)調(diào)用處呢?
? ? ? ?復(fù)現(xiàn)問題時(shí),為啥會(huì)中斷在DebugBreak函數(shù)調(diào)用處呢?查看了函數(shù)調(diào)用堆棧中函數(shù)在WebRTC中的源碼,malloc返回失敗的代碼如下所示:
1)申請(qǐng)內(nèi)存的malloc返回NULL:
2)malloc返回NULL,會(huì)執(zhí)行到RTC_CHECK宏中的rtc_FatalMessage接口:?
3)緊接著調(diào)用到FatalLog接口:?
4)?FatalLog接口的實(shí)現(xiàn)如下:
我們?cè)贔atalLog接口的結(jié)尾處我們看到了DebugBreak系統(tǒng)API接口的調(diào)用,然后緊接著調(diào)用C函數(shù)abort。
? ? ? ?Windows API接口DebugBreak的作用就是讓正在調(diào)試的調(diào)試器中斷下來,目的是讓調(diào)試器感知到當(dāng)前的事件,所以Windbg中斷在DebugBreak函數(shù)的調(diào)用處。此外,在調(diào)用DebugBreak接口后,會(huì)緊接著調(diào)用abort接口將當(dāng)前進(jìn)程終止掉,應(yīng)該是WebRTC內(nèi)部認(rèn)為內(nèi)存申請(qǐng)失敗了,業(yè)務(wù)沒法正常展開了,程序沒法正常運(yùn)行,沒有繼續(xù)存活下去的意義了,所以強(qiáng)行將程序進(jìn)程終止了。
? ? ? ?調(diào)用C函數(shù)malloc申請(qǐng)內(nèi)存申請(qǐng)失敗時(shí),malloc會(huì)返回NULL,不會(huì)拋出異常;如果使用new去申請(qǐng),申請(qǐng)失敗時(shí)默認(rèn)會(huì)拋出bad_alloc異常,如果程序中沒有處理這個(gè)異常,則會(huì)導(dǎo)致程序發(fā)生異常崩潰。當(dāng)然,我們可以在new時(shí)使用nothrow,不讓其拋出異常,返回NULL,示例代碼如下:
#include <iostream>int main(){char *p = NULL;int i = 0;do{p = new(std::nothrow) char[10*1024*1024]; // 每次申請(qǐng)10MBi++;Sleep(5);}while(p);if(NULL == p){std::cout << "分配了 " << (i-1)*10 << " M內(nèi)存" //分配了 1890 Mn內(nèi)存第 1891 次內(nèi)存分配失敗 << "第 " << i << " 次內(nèi)存分配失敗";}return 0;
}
? ? ? ?另外,abort的調(diào)用也會(huì)讓正在調(diào)試的Windbg中斷下來,我們來看看abort函數(shù)的內(nèi)部實(shí)現(xiàn):
/***
*void abort() - abort the current program by raising SIGABRT
*
*Purpose:
* print out an abort message and raise the SIGABRT signal. If the user
* hasn't defined an abort handler routine, terminate the program
* with exit status of 3 without cleaning up.
*
* Multi-thread version does not raise SIGABRT -- this isn't supported
* under multi-thread.
*******************************************************************************/
void __cdecl abort (void)
{_PHNDLR sigabrt_act = SIG_DFL;#ifdef _DEBUGif (__abort_behavior & _WRITE_ABORT_MSG){/* write the abort message */_NMSG_WRITE(_RT_ABORT);}
#endif /* _DEBUG *//* Check if the user installed a handler for SIGABRT.* We need to read the user handler atomically in the case* another thread is aborting while we change the signal* handler.*/sigabrt_act = __get_sigabrt();if (sigabrt_act != SIG_DFL){raise(SIGABRT);}/* If there is no user handler for SIGABRT or if the user* handler returns, then exit from the program anyway*/if (__abort_behavior & _CALL_REPORTFAULT){_call_reportfault(_CRT_DEBUGGER_ABORT, STATUS_FATAL_APP_EXIT, EXCEPTION_NONCONTINUABLE);}/* If we don't want to call ReportFault, then we call _exit(3), which is the* same as invoking the default handler for SIGABRT*/_exit(3);
}
上述代碼中先調(diào)用了raise(SIGABRT),該函數(shù)是觸發(fā)一個(gè)SIGABRT信號(hào)終止異常,如果當(dāng)前正在調(diào)試狀態(tài),會(huì)讓調(diào)試器中斷下來。接下來調(diào)用C函數(shù)_exit退出當(dāng)前進(jìn)程。?
? ? ? ?關(guān)于調(diào)用abort強(qiáng)制終止程序?qū)е鲁绦蜷W退的案例,還可以查看我之前的文章:
C++程序中執(zhí)行abort等操作導(dǎo)致沒有生成dump文件的問題案例分析https://blog.csdn.net/chenlycly/article/details/129003869
5、占用程序進(jìn)程的虛擬內(nèi)存的因素有哪些??? ?
? ? ? 占用進(jìn)程的虛擬內(nèi)存空間的因素有很多,這里大概地羅列幾個(gè),大家可以簡單地了解一下。我們可以從程序的五大內(nèi)存分區(qū)的角度去看,程序的五大內(nèi)存分區(qū)如下:
5.1、二進(jìn)制文件
? ? ? ?主程序及主程序依賴的二進(jìn)制庫文件,都需要加載到進(jìn)程空間中,都占用一定的虛擬內(nèi)存。exe主程序依賴底層的多個(gè)業(yè)務(wù)庫和系統(tǒng)dll庫,比如業(yè)務(wù)庫有組件dll庫、協(xié)議dll庫、網(wǎng)絡(luò)dll庫、開源庫等,可能會(huì)依賴上百個(gè)dll庫。這些dll庫在主程序啟動(dòng)時(shí),會(huì)加載到程序進(jìn)程的進(jìn)程空間中,會(huì)占用進(jìn)程的虛擬內(nèi)存空間,屬于代碼段的虛擬內(nèi)存。
? ? ? ?如果能減少dll庫的依賴,減小依賴的dll庫文件的大小,可以減少程序?qū)μ摂M內(nèi)存的占用。特別對(duì)于一些大型的開源庫,一方面要減少程序安裝包的大小,另一方面減少二進(jìn)制文件對(duì)虛擬內(nèi)存的占用,需要進(jìn)行一些裁剪,開源庫中也提供了一些宏開關(guān)和編譯選項(xiàng)。比如google開源的嵌入式瀏覽器框架庫libcef,默認(rèn)是比較大的,編譯后的dll庫要占到好幾十MB,可以對(duì)該庫進(jìn)行裁剪。有時(shí)為了進(jìn)行深度的裁剪和優(yōu)化,甚至?xí)ブ苯有薷拈_源代碼。
5.2、線程的??臻g
? ? ? ?程序中創(chuàng)建了多個(gè)線程(多個(gè)模塊都創(chuàng)建了線程),每個(gè)線程都要分配對(duì)應(yīng)的??臻g,線程越多占用的??臻g越多,這是??臻g也是從進(jìn)程的虛擬內(nèi)存上劃撥的。但對(duì)于應(yīng)用程序,開多個(gè)線程去并行處理任務(wù),也是必不可少的。
對(duì)于線程占用的棧空間大小,Windows下每個(gè)線程默認(rèn)的??臻g大小是1MB,Linux下每個(gè)線程的默認(rèn)的棧空間是8MB。
5.3、程序中申請(qǐng)的堆內(nèi)存
? ? ? ? C++程序中使用的大部分內(nèi)存都是堆內(nèi)存,堆內(nèi)存占總虛擬內(nèi)存的大部分。堆內(nèi)存是通過使用new或者調(diào)用malloc等函數(shù)申請(qǐng)的。
在Windows平臺(tái)上,動(dòng)態(tài)申請(qǐng)內(nèi)存的方式有多種,比如使用new(要用delete去釋放),比如使用malloc(要用free去釋放),再比如調(diào)用系統(tǒng)API函數(shù)HeapCreate或者HeapAlloc(要用HeapFree去釋放),還有可以調(diào)用API函數(shù)VirtualAlloc(要用VirtualFree去釋放),還有其他的API函數(shù)。
? ? ? ?要盡量節(jié)約內(nèi)存,按需分配,使用的buffer大小可能會(huì)動(dòng)態(tài)變化,頻繁動(dòng)態(tài)地去申請(qǐng)和釋放內(nèi)存,可能會(huì)產(chǎn)生很多內(nèi)存碎片。這也是上來就申請(qǐng)固定長度的buffer的好處,可以減小生成內(nèi)存碎片。使用內(nèi)存池可以減少內(nèi)存碎片,但對(duì)于本問題,確實(shí)需要使用很多虛擬內(nèi)存,使用內(nèi)存池減少內(nèi)存碎片作用也不是很大。
6、當(dāng)前用戶態(tài)虛擬內(nèi)存占用高的解決辦法
? ? ? ?WebRTC開源庫比較大,會(huì)消耗很多的內(nèi)存,如何解決WebRTC占用大量虛擬內(nèi)存的問題,有如下的方法。
6.1、修改WebRTC編譯選項(xiàng),減少內(nèi)存占用
? ? ? ?可以嘗試修改WebRTC編譯選項(xiàng),對(duì)其進(jìn)行裁剪縮編,釋放出一些占用內(nèi)存的代碼,但這種做法降低內(nèi)存的效果有限,因?yàn)閃ebRTC作為大型庫本來就需要占用大量的內(nèi)存資源。
6.2、將程序做成64位的
? ? ? ?可以將主程序做成64位的,64位程序的用戶態(tài)虛擬內(nèi)存非常大,可以“肆無忌憚”的使用。但占用的虛擬內(nèi)存過大,在代碼執(zhí)行過程中虛擬內(nèi)存要切換到物理內(nèi)存上,會(huì)來回頻繁地切換,也會(huì)影響程序的執(zhí)行效率。此外,物理內(nèi)存較小,也會(huì)影響虛擬內(nèi)存到物理內(nèi)存的切換,也會(huì)顯著降低程序的運(yùn)行速度。
6.2.1、我們的程序?yàn)榱思嫒?2位系統(tǒng),需要做成32位的
? ? ? ?我們的程序之所以還是32位的,是因?yàn)槲覀冃枰嫒?2位系統(tǒng)。有些人說,為啥不直接將主程序做成x64(64位程序)的呢?因?yàn)槲覀兊某绦蜻€要兼容32位的系統(tǒng),雖然現(xiàn)在普遍使用的Win10和Win11都是64位的,但還有少部分客戶在使用32位的Win7系統(tǒng),什么樣的客戶都有,不排除有使用32位系統(tǒng)的可能。
64位程序是沒法在32位系統(tǒng)上運(yùn)行的,但32位程序可以在64位系統(tǒng)上運(yùn)行,這是操作系統(tǒng)去做兼容的。
6.2.2、關(guān)于32位程序和64位程序的說明
? ? ? ?32位exe或dll是不能和64位exe或exe文件混用的,系統(tǒng)是嚴(yán)格區(qū)分二進(jìn)制文件位數(shù)的,32位exe或dll文件只能使用(依賴)32位exe或dll文件,64位exe或dll文件只能使用(依賴)64位的exe或dll文件。比如32位的exe主程序如果使用64位dll庫,則啟動(dòng)時(shí)會(huì)報(bào)錯(cuò)。
? ? ? ?對(duì)于Windows系統(tǒng)庫,64位系統(tǒng)的系統(tǒng)庫都有兩個(gè)版本的,分別是32位系統(tǒng)庫和64位系統(tǒng)庫。32位的系統(tǒng)庫則存放在C:\Windows\SysWOW64目錄中(注意WOW64不是64位系統(tǒng)庫目錄,WOW64是32位程序運(yùn)行在64位系統(tǒng)上的意思);64位系統(tǒng)庫存放在C:\Windows\System32目錄中:
? ? ? ?所以,32位程序依賴的32位系統(tǒng)庫都在C:\Windows\SysWOW64目錄中。這個(gè)SysWOW64和system32目錄很容易混淆,前幾天還有個(gè)同事問我這兩個(gè)目錄對(duì)應(yīng)關(guān)系到底是啥,他之前一度以為system32目錄下的是32位系統(tǒng)庫,SysWOW64目錄下的是64位系統(tǒng)庫,事實(shí)卻正好相反。
? ? ? ?當(dāng)32位程序在64位Windows上運(yùn)行時(shí),會(huì)有個(gè)重定向問題,可以查看我之前的文章:
關(guān)于32位程序在64位系統(tǒng)下運(yùn)行中需要注意的重定向問題(有圖有真相)https://blog.csdn.net/chenlycly/article/details/53119127Win7用戶帳戶控制數(shù)據(jù)重定向
https://blog.csdn.net/chenlycly/article/details/53408212
6.3、使用Visual Studio的鏈接選項(xiàng),將用戶態(tài)虛擬內(nèi)存從2GB擴(kuò)充到3GB
? ? ? ?可以在Visual Studio鏈接選項(xiàng)中打開擴(kuò)大用戶態(tài)虛擬內(nèi)存的選項(xiàng)/L largeAddressAware,如下所示:
這樣可以將用戶態(tài)虛擬內(nèi)存擴(kuò)到3GB,這樣可以有效緩解內(nèi)存不夠用的問題。
? ? ? ?32進(jìn)程只有4GB的虛擬內(nèi)存,如果將用戶態(tài)虛擬內(nèi)存由2GB擴(kuò)到3GB,內(nèi)核態(tài)的虛擬內(nèi)存應(yīng)該會(huì)被壓縮到1GB,這樣會(huì)不會(huì)導(dǎo)致內(nèi)核態(tài)的代碼執(zhí)行比較慢,導(dǎo)致程序的運(yùn)行性能下降呢?可能運(yùn)行性能會(huì)有一定的損失,但既然系統(tǒng)運(yùn)行這種擴(kuò)充用戶態(tài)虛擬內(nèi)存的方式,應(yīng)該影響不會(huì)很大。
6.4、使用多進(jìn)程模式
? ? ? ?但上述方法,在使用WebRTC開源庫時(shí)可能有問題,如果要解更多路數(shù)的視頻,會(huì)占用更多的內(nèi)存??梢钥紤]將WebRTC封裝成進(jìn)程,使用多進(jìn)程的模式,主進(jìn)程與WebRTC進(jìn)程使用RPC方式進(jìn)行接口的調(diào)用。像Chrome那樣,搞多個(gè)進(jìn)程,不同的進(jìn)程處理不同的事務(wù),一個(gè)進(jìn)程崩潰了,不會(huì)影響到主進(jìn)程,將崩潰的進(jìn)程重新啟動(dòng)。但是多個(gè)進(jìn)程之間需要通信,需要協(xié)同控制,控制不好也容易出問題。進(jìn)程之間如何高效地的傳遞數(shù)據(jù)也是個(gè)問題,這都需要人力和技術(shù)去支撐。但多進(jìn)程模式將是最終且最穩(wěn)妥的解決方案。
7、最后
? ? ? ?針對(duì)我們遇到的上述問題,目前的做法是,一邊優(yōu)化內(nèi)存占用,一邊使用擴(kuò)大用戶態(tài)虛擬內(nèi)存的做法,同步進(jìn)行。