欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C/C++內(nèi)存泄漏原因分析與應(yīng)對(duì)方法

 更新時(shí)間:2023年07月14日 10:08:08   作者:魚(yú)在樹(shù)上飛  
內(nèi)存泄漏會(huì)導(dǎo)致當(dāng)前應(yīng)用程序消耗更多的內(nèi)存,使得其他應(yīng)用程序可用的內(nèi)存更少了,那么為什么會(huì)內(nèi)存泄漏,我們應(yīng)該怎樣應(yīng)對(duì)內(nèi)存泄漏,所以接下來(lái)就給大家詳細(xì)介紹一下C++內(nèi)存泄漏原因分析與應(yīng)對(duì)方法,需要的朋友可以參考下

內(nèi)存泄漏

一、內(nèi)存泄漏的危害:

內(nèi)存泄漏會(huì)導(dǎo)致當(dāng)前應(yīng)用程序消耗更多的內(nèi)存,使得其他應(yīng)用程序可用的內(nèi)存更少了。

如果有個(gè)進(jìn)程可用的內(nèi)存不夠,就會(huì)觸發(fā)Linux操作系統(tǒng)的直接/后臺(tái)內(nèi)存回收(即將一些內(nèi)存頁(yè)的數(shù)據(jù)寫(xiě)到磁盤(pán)里,那么該頁(yè)也就可用了,臟頁(yè)回寫(xiě))。雖然后臺(tái)回收是異步的不阻塞當(dāng)前進(jìn)程,但是內(nèi)存還是不夠會(huì)觸發(fā)直接內(nèi)存回收,最后內(nèi)存泄漏積累到一定程度,會(huì)直接觸發(fā)OOM,該機(jī)制會(huì)殺掉那些實(shí)時(shí)占用內(nèi)存大的進(jìn)程。

而且,即使沒(méi)有OOM,無(wú)論是直接回收還是后臺(tái)回收,都需要磁盤(pán)I/O而且需要多執(zhí)行額外的回收線(xiàn)程,使系統(tǒng)性能下降。

  • 后臺(tái)內(nèi)存回收(kswapd):在物理內(nèi)存緊張的時(shí)候,會(huì)喚醒 kswapd 內(nèi)核線(xiàn)程來(lái)回收內(nèi)存,這個(gè)回收內(nèi)存的過(guò)程異步的,不會(huì)阻塞進(jìn)程的執(zhí)行。
  • 直接內(nèi)存回收(direct reclaim):如果后臺(tái)異步回收跟不上進(jìn)程內(nèi)存申請(qǐng)的速度,就會(huì)開(kāi)始直接回收,這個(gè)回收內(nèi)存的過(guò)程是同步的,會(huì)阻塞進(jìn)程的執(zhí)行,這個(gè)過(guò)程比較慢,導(dǎo)致CPU占用率飆升。

如果直接內(nèi)存回收后,空閑的物理內(nèi)存仍然無(wú)法滿(mǎn)足此次物理內(nèi)存的申請(qǐng),那么內(nèi)核就會(huì)放最后的大招了 ——觸發(fā) OOM (Out of Memory)機(jī)制

還有資源泄漏:

比如沒(méi)有關(guān)閉文件,程序提前return或報(bào)錯(cuò)或忘記關(guān)閉,則可能導(dǎo)致想寫(xiě)入文件的數(shù)據(jù)沒(méi)有真正落盤(pán),從而丟失數(shù)據(jù)。

二、內(nèi)存泄漏舉例:

1,在free()前就返回了,或者是報(bào)錯(cuò)并退出程序。要在程序的所有路徑上(if()的各個(gè)條件)都執(zhí)行資源釋放操作。

2,在析構(gòu)函數(shù)中未執(zhí)行內(nèi)存釋放操作。在構(gòu)造函數(shù)中申請(qǐng)了堆內(nèi)存或者打開(kāi)了文件,在析構(gòu)函數(shù)中忘了釋放資源。

3,基類(lèi)的析構(gòu)函數(shù)未聲明為虛函數(shù)。

析構(gòu)函數(shù)如果不聲明為虛函數(shù),可能會(huì)導(dǎo)致多態(tài)對(duì)象在刪除時(shí)無(wú)法正確調(diào)用派生類(lèi)的析構(gòu)函數(shù)(如果子類(lèi)構(gòu)造函數(shù)里malloc()了內(nèi)存,然后在析構(gòu)函數(shù)里free()),從而導(dǎo)致內(nèi)存泄漏。

4,shared_ptr循環(huán)引用導(dǎo)致內(nèi)存泄漏,用weak_ptr解決。如下示例:

class Contro {
private:
	double* p;
 public:
  Contro() {
  	 p = new double[10];
  }
  ~Controller() {
  	 delete[] p;
     std::cout << "in ~Contro" << std::endl;
  }
// 類(lèi)內(nèi)類(lèi)
  class SubContro {
   public:
    SubContro() {
  	  p = new double[10];
  }
    ~SubContro() {
      delete[] p;
      std::cout << "in ~SubContro" << std::endl;
    }
    std::shared_ptr<Contro> controller_;
  };
  std::shared_ptr<SubContro> sub_controller_;
};
int main() {
  auto contro = std::make_shared<Contro>();
  auto sub_contro = std::make_shared<Controller::SubContro>();
  contro->sub_controller_ = sub_contro;
  sub_contro->controller_ = contro;
  // 打印引用計(jì)數(shù)
  std::cout << "controller use_count: " << contro.use_count() << std::endl;
  std::cout << "sub_controller use_count: " << sub_contro.use_count() << std::endl;
  return 0;
}

發(fā)生循環(huán)引用,兩個(gè)的引用計(jì)數(shù)輸出都是2,所以main函數(shù)結(jié)束的時(shí)候,引用計(jì)數(shù)沒(méi)有減為0,就不會(huì)調(diào)用二者的析構(gòu)函數(shù),導(dǎo)致資源泄漏。
將SubContro類(lèi)里的shared_ptr改成weak_ptr即可,后者不會(huì)增加引用計(jì)數(shù),因此兩個(gè)智能指針的引用計(jì)數(shù)都是1,然后main結(jié)束的時(shí)候,引用計(jì)數(shù)減少為0,然后執(zhí)行析構(gòu)函數(shù),此時(shí)不會(huì)發(fā)生內(nèi)存泄漏,輸出如下:

contro use_count: 1
sub_contro use_count: 2
in ~Contro
in ~SubContro

三、避免內(nèi)存泄漏的手段:

1. 靜態(tài)代碼檢查工具

(1)對(duì)于大型項(xiàng)目,可以使用啄木鳥(niǎo)這種靜態(tài)代碼分析工具

靜態(tài)代碼檢查工具會(huì)從詞法、語(yǔ)法、語(yǔ)義等多維度去對(duì)工程代碼掃描分析,發(fā)現(xiàn)可能存在的問(wèn)題,比如變量未定義、類(lèi)型不匹配、變量作用域問(wèn)題、數(shù)組下標(biāo)越界、內(nèi)存泄露等問(wèn)題。

既然是靜態(tài),那么就不是運(yùn)行時(shí)。但是是編譯前還是編譯后還是編譯中?

其實(shí)都有,啄木鳥(niǎo)是給源文件就行,然后它會(huì)在編譯的過(guò)程中去檢測(cè)語(yǔ)法,詞法,以及最后生成的二進(jìn)制。

代碼靜態(tài)分析(SAST):可以簡(jiǎn)單的理解為在不執(zhí)行程序的情況下,對(duì)源代碼, 中間代碼或者二進(jìn)制代碼進(jìn)行分析的技術(shù)

(2)編譯成專(zhuān)門(mén)的內(nèi)存泄漏檢查版本。

可以把整個(gè)項(xiàng)目編譯成檢查內(nèi)存泄漏版本的可執(zhí)行文件,然后運(yùn)行相關(guān)工具,并且讓運(yùn)行結(jié)果專(zhuān)門(mén)記錄內(nèi)存泄漏,將泄漏結(jié)果放在對(duì)應(yīng)輸出文件上。

比如opengauss就有,參考鏈接:http://t.csdn.cn/DqusO

編譯openGauss時(shí),編譯一個(gè)memcheck版的,然后通過(guò)跑fastcheck_single來(lái)發(fā)現(xiàn)代碼中的內(nèi)存問(wèn)題。 編譯方式和編譯普通的openGauss基本一致,只是在configure時(shí),添加一個(gè) --enable-memory-check 參數(shù),編譯出來(lái)的就是memcheck版本的openGauss。

但是編譯前,要設(shè)置一些環(huán)境變量,ulimit -v unlimited

ulimit命令:用于控制shell程序的資源, -v <虛擬內(nèi)存大小>  指定可使用的虛擬內(nèi)存上限,單位為KB。

因?yàn)榭赡苡袃?nèi)存泄漏,所以就設(shè)置虛擬內(nèi)存大小為不受限制。

2. valgrind工具

可以安裝valgrind工具,指定工具--tool=memcheck,也可以指定輸出日志,否則輸出在終端

--log-file=leak1.log

執(zhí)行命令如下:

valgrind  --tool=memcheck ./a.out

如下可以看到malloc和free的次數(shù),以及被申請(qǐng)的字節(jié)數(shù):

HEAP SUMMARY:
in use at exit: 8 bytes in 1 blocks
total heap usage: 1 allocs, 0 frees, 8 bytes allocated

這個(gè)工具的用法還挺多,可以參考https://zhuanlan.zhihu.com/p/92074597

3. GDB調(diào)試

比如我們懷疑FUNC()函數(shù)有內(nèi)存泄漏。

1,比如給某個(gè)函數(shù)FUNC()打斷點(diǎn),進(jìn)入后這個(gè)函數(shù)里面也調(diào)用了很多其他函數(shù)func1,func2…,懷疑這些調(diào)用里面,或者外面有內(nèi)存泄漏。我們可以給malloc()和free打斷點(diǎn)(或者是自己封裝的函數(shù)),當(dāng)malloc()命中后,bt查看棧幀,就知道哪個(gè)函數(shù)調(diào)用了malloc,申請(qǐng)了堆內(nèi)存,比如func1,這樣可以重點(diǎn)關(guān)注該函數(shù)。

2,然后看后面free()斷點(diǎn)有沒(méi)有命中,命中的時(shí)候查看棧幀,如果不是這個(gè)函數(shù)func1調(diào)用的free(),那說(shuō)明這個(gè)函數(shù)沒(méi)有執(zhí)行free。

3,此外,可以追蹤指針p的值(watch p),看看它有沒(méi)有變?yōu)?x0,被釋放且被賦值為0x0,才不會(huì)成為懸空指針。

4,在函數(shù)FUNC()的末尾,還可以看看malloc和free的斷點(diǎn)命中次數(shù),如果次數(shù)一樣,那沒(méi)問(wèn)題。

以上就是C/C++內(nèi)存泄漏原因分析與應(yīng)對(duì)方法的詳細(xì)內(nèi)容,更多關(guān)于C/C++內(nèi)存泄漏的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C++實(shí)現(xiàn)投骰子的隨機(jī)游戲

    C++實(shí)現(xiàn)投骰子的隨機(jī)游戲

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)投骰子的隨機(jī)游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • C++使用CriticalSection實(shí)現(xiàn)線(xiàn)程同步實(shí)例

    C++使用CriticalSection實(shí)現(xiàn)線(xiàn)程同步實(shí)例

    這篇文章主要介紹了C++使用CriticalSection實(shí)現(xiàn)線(xiàn)程同步實(shí)例,是使用CriticalSection對(duì)前文實(shí)例的擴(kuò)展,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2014-10-10
  • C++ 復(fù)制控制之復(fù)制構(gòu)造函數(shù)的實(shí)現(xiàn)

    C++ 復(fù)制控制之復(fù)制構(gòu)造函數(shù)的實(shí)現(xiàn)

    所謂的“復(fù)制控制”即通過(guò)這三個(gè)成員函數(shù)控制對(duì)象復(fù)制的過(guò)程,本文主要介紹了C++ 復(fù)制控制之復(fù)制構(gòu)造函數(shù)的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-11-11
  • Qt實(shí)現(xiàn)模糊匹配功能的實(shí)例詳解

    Qt實(shí)現(xiàn)模糊匹配功能的實(shí)例詳解

    對(duì)于瀏覽器的使用,我想大家一定不會(huì)陌生吧,輸入要搜索的內(nèi)容時(shí),會(huì)出現(xiàn)相應(yīng)的匹配信息。本文就來(lái)用Qt實(shí)現(xiàn)模糊匹配功能,感興趣的可以了解一下
    2022-10-10
  • Qt項(xiàng)目實(shí)戰(zhàn)之實(shí)現(xiàn)多文本編輯器

    Qt項(xiàng)目實(shí)戰(zhàn)之實(shí)現(xiàn)多文本編輯器

    這篇文章主要為大家詳細(xì)介紹了如何利用Qt實(shí)現(xiàn)簡(jiǎn)易的多文本編輯器,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴可以了解一下
    2023-03-03
  • C語(yǔ)言超詳細(xì)講解指針與結(jié)構(gòu)體

    C語(yǔ)言超詳細(xì)講解指針與結(jié)構(gòu)體

    指針提供了對(duì)地址操作的一種方法,因此,使用指針可使得C語(yǔ)言能夠更高效地實(shí)現(xiàn)對(duì)計(jì)算機(jī)底層硬件的操作。另外,通過(guò)指針可以更便捷地操作數(shù)組。C數(shù)組允許定義可存儲(chǔ)相同類(lèi)型數(shù)據(jù)項(xiàng)的變量,結(jié)構(gòu)是C編程中另一種用戶(hù)自定義的可用的數(shù)據(jù)類(lèi)型,它允許您存儲(chǔ)不同類(lèi)型的數(shù)據(jù)項(xiàng)
    2022-05-05
  • C語(yǔ)言獲取Linux系統(tǒng)精確時(shí)間的方法

    C語(yǔ)言獲取Linux系統(tǒng)精確時(shí)間的方法

    下面小編就為大家?guī)?lái)一篇C語(yǔ)言獲取Linux系統(tǒng)精確時(shí)間的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • C++11之std::future對(duì)象的使用以及說(shuō)明

    C++11之std::future對(duì)象的使用以及說(shuō)明

    這篇文章主要介紹了C++11之std::future對(duì)象的使用以及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • C語(yǔ)言自定義類(lèi)型超詳細(xì)梳理之結(jié)構(gòu)體 枚舉 聯(lián)合體

    C語(yǔ)言自定義類(lèi)型超詳細(xì)梳理之結(jié)構(gòu)體 枚舉 聯(lián)合體

    今天我們來(lái)學(xué)習(xí)一下自定義類(lèi)型,自定義類(lèi)型包括結(jié)構(gòu)體、枚舉、聯(lián)合體,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考
    2022-03-03
  • 淺析C語(yǔ)言中對(duì)于char*和char[]的理解

    淺析C語(yǔ)言中對(duì)于char*和char[]的理解

    char * s 只是一個(gè)保存字符串首地址的指針變量,char a[]是許多連續(xù)的內(nèi)存單元,單元中的元素是char型,char * 和 char a[]具有相同的效果,源于字符串的本質(zhì),這篇文章主要介紹了C語(yǔ)言中對(duì)于char*和char[]的理解,需要的朋友可以參考下
    2023-02-02

最新評(píng)論