成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

怎么解決內(nèi)存泄漏問題

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

??祍sl適用于網(wǎng)站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為成都創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書合作)期待與您的合作!

問題排查

首先確定內(nèi)存泄漏問題出現(xiàn)的時間,發(fā)現(xiàn)在該時間點的上線有兩次代碼提交,其中一個就是我的。于是立刻排查這兩次代碼的改動,確定了另一個同事的代碼不可能會有內(nèi)存問題后(因為另一個同事的上線僅僅修改了配置)我知道肯定是自己的代碼出現(xiàn)了問題。

確定了問題所在后趕緊把自己的代碼回滾掉,接下來就可以放心debug了。

Debug

什么是內(nèi)存泄漏?

簡單的講就是程序員申請的內(nèi)存在使用完后沒有還給操作系統(tǒng),由于筆者使用的是C++語言,因此內(nèi)存泄漏一般是這樣的:

obj* o = new obj();  ...  // 使用完obj后沒有delete掉

肯定有什么地方申請了內(nèi)存后沒有調(diào)用delete釋放內(nèi)存。

在這里介紹一下筆者的代碼改動,我的任務(wù)其實是重構(gòu)一段代碼,把這段代碼并行化。也就是舊的邏輯是在一個線程中串行執(zhí)行的,現(xiàn)在我要把這段邏輯放到兩個線程中并行執(zhí)行,這是最讓人頭疼的任務(wù)之一,并行化改造是比較容易出bug的。

接下來梳理了一遍中所有內(nèi)存的申請和釋放,這其中包括:

  •  使用new/delete分配釋放的內(nèi)存

  •  使用內(nèi)存池分配釋放的內(nèi)存

仔細梳理一遍后沒有發(fā)現(xiàn)任何問題,該釋放的內(nèi)存都已經(jīng)釋放掉了,這時筆者已經(jīng)開始懷疑人生了 :) ,很顯然還有一段沒有注意到的地方出現(xiàn)了問題,這是必然的,雖然知道問題必然出現(xiàn)在改動的這些代碼里但是我并不能確定出現(xiàn)的位置。

沒有辦法,到這里基本上已經(jīng)要放棄自己人肉debug了,想利用一些內(nèi)存檢測工具來幫助自己確定問題。

常見的內(nèi)存泄漏檢測工具包括valgrind、gperftools等,valgrind的好處在于無需重新編譯代碼即可進行內(nèi)存檢測,但是缺點是會使得程序運行非常緩慢,官方文檔給的說法是會比正常的程序運行慢20-30倍;gperftools則需要重新編譯可執(zhí)行程序。這些工具需要下載安裝測試,其中還涉及到申請機器權(quán)限等問題,筆者覺得還是比較麻煩,況且這個問題也不是大海撈針一樣,問題肯定出在了并行化的這段代碼中。

到這里我決定再換一個思路來排查問題,既然代碼重構(gòu)后開始并行執(zhí)行,那么出現(xiàn)問題大概率是因為多線程問題,遇到多線程問題首先重點排查的就是線程間的共享數(shù)據(jù)。

多線程問題的關(guān)鍵——共享數(shù)據(jù)

我們知道如果線程之間沒有共享數(shù)據(jù)那么就不會有線程安全問題,我們使用的鎖、信號量、條件變量等其實都是用來保護共享數(shù)據(jù)的,比如鎖通常是用來包括臨界區(qū)的,臨界區(qū)中的代碼操作的就是線程共享數(shù)據(jù);信號量使用的一個經(jīng)典場景就是生產(chǎn)者消費者問題,生產(chǎn)者線程以及消費者線程都會操作同一個隊列,這里的隊列就是共享數(shù)據(jù)。

沿著這個思路開始找在兩個線程中都使用到的共享數(shù)據(jù),果不其然,在一個角落中發(fā)現(xiàn)了這樣一段代碼:

auto* pb = global->mutable_obj();

這是分配protobuf對象的一段代碼,protobuf是Google開發(fā)是一種類似于JSON、XML的技術(shù),因此常用于網(wǎng)絡(luò)通信和數(shù)據(jù)交換等場景,比如RPC等。

如果你不了解protobuf也沒有關(guān)系,實際上上面的這段代碼的要做的事情是這樣的:

if (global->obj == NULL) {    global->obj = new obj();  }  return global->obj;

值得注意的是這段代碼現(xiàn)在會在兩個線程中執(zhí)行,顯然問題就出現(xiàn)在了這里。

那么問題是怎么出現(xiàn)的呢?

我們假設(shè)有兩個線程,線程A和線程B,當這樣一段代碼在線程AB中同時執(zhí)行時可能會有以下場景:

  •  線程A拿到global->obj并檢測到此時的global->obj為空,因此決定為其分配內(nèi)存,但不巧的是此時發(fā)生線程切換,線程A在為global->obj分配內(nèi)存前被暫停運行,如下所示: 

if (global->obj == NULL) {      <------- 線程切換,線程A被暫停執(zhí)行       global->obj = new obj();  }  return global->obj;
  •  線程A被暫停運行后線程B開始執(zhí)行,這段代碼同樣會在線程B中執(zhí)行一遍,因此線程B會首先檢查global->obj發(fā)現(xiàn)為空,因此為global->obj分配內(nèi)存,分配完內(nèi)存后發(fā)生線程切換,線程B被暫停運行,如下所示: 

if (global->obj == NULL) {      global->obj = new obj();      <------- 線程切換,線程B被暫停執(zhí)行   }  return global->obj;
  • 線程B被暫停運行后調(diào)度器決定重新運行線程A,此時線程A開始從被中斷的地方繼續(xù)運行,還記得線程A是從哪里被中斷的嗎,沒錯,就是在為global->obj分配內(nèi)存前被中斷的,此時線程A繼續(xù)運行,也就是說global->obj = new obj()這段代碼又被執(zhí)行了一次,雖然線程B已經(jīng)為global->obj分配了內(nèi)存。

Oops,典型的內(nèi)存泄漏,線程B分配的內(nèi)存再也無法被正常釋放掉了。

至此,我們已經(jīng)找到了問題的原因,罪魁禍首就是共享數(shù)據(jù),關(guān)鍵的一點是要意識到你的線程會隨時被中斷執(zhí)行,CPU會隨時切換到其它線程。

代碼修復(fù)也非常簡單,再新增一個變量,兩個線程不在使用共享數(shù)據(jù),到這里問題就解決了,從發(fā)現(xiàn)問題到完成修復(fù)耗時大概4小時。

經(jīng)驗教訓(xùn)

代碼的并行化重構(gòu)是一件非常棘手的任務(wù),很容易出現(xiàn)線程安全問題,解決線程安全問題首先要考慮的不是要不要加鎖,而是多個線程是否真的有必要使用共享數(shù)據(jù),沒有必要的話多個線程操作私有數(shù)據(jù)根本就不會出現(xiàn)線程安全問題。

當出現(xiàn)線程安全問題時,第一時間重點排查線程使用的共享數(shù)據(jù)。

內(nèi)存泄漏檢測工具

雖然這些沒有使用檢測工具全靠人肉debug其實還是因為問題排查范圍比較小,如果我們根本就不知道問題出現(xiàn)在了那次代碼改動那么檢測工具就非常重要了,在這里簡單介紹一下valgrind的使用,詳細的介紹請參考官方文檔。

假設(shè)有這樣一段問題代碼:

#include <stdlib.h>  void f(void)    {     int* x = malloc(10 * sizeof(int));     x[10] = 0;        // 問題1: 越界  }                    // 問題2: 內(nèi)存泄漏,x沒有被釋放掉   int main()   {     f();     return 0;  }

這段代碼中有兩個問題:一個是數(shù)據(jù)的越界訪問;另一個是內(nèi)存泄漏。將該程序編譯為myprog。

接下來使用valgrind來檢查該程序,使用以下命令:

valgrind --leak-check=yes myprog

運行完成后valgrind會給出檢測報告,關(guān)于程序越界訪問會給出這樣的輸出:

==19182== Invalid write of size 4  ==19182==    at 0x804838F: f (example.c:6)  ==19182==    by 0x80483AB: main (example.c:11)  ==19182==  Address 0x1BA45050 is 0 bytes after a block of size 40 alloc'd  ==19182==    at 0x1B8FF5CD: malloc (vg_replace_malloc.c:130) ==19182==    by 0x8048385: f (example.c:5)  ==19182==    by 0x80483AB: main (example.c:11)

第一行告訴你代碼中存在Invalid write,也就是無效的寫,并給出了問題出現(xiàn)的位置。

關(guān)于內(nèi)存泄漏問題會給出這樣的輸出:

==19182== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1  ==19182==    at 0x1B8FF5CD: malloc (vg_replace_malloc.c:130)  ==19182==    by 0x8048385: f (example.c:5)  ==19182==    by 0x80483AB: main (example.c:11)

這里第一行報告了內(nèi)存"definitely lost",也就是說一定會存在內(nèi)存泄漏,并給出了問題出現(xiàn)的位置。

實際上除了"definitely lost",valgrind還會給出"probably lost"的報告,這兩種報告的含義是這樣的:

  •  "definitely lost":你的程序一定存在內(nèi)存泄漏問題,修復(fù)。

  •  "probably lost":你的程序看起來像是有內(nèi)存泄漏,有可能你在使用指針完成一些特定操作,因此不一定100%存在問題。

“怎么解決內(nèi)存泄漏問題”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

文章名稱:怎么解決內(nèi)存泄漏問題
網(wǎng)頁路徑:http://jinyejixie.com/article0/jjpcio.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護、搜索引擎優(yōu)化網(wǎng)站設(shè)計、App開發(fā)企業(yè)建站、品牌網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

營銷型網(wǎng)站建設(shè)
巴林右旗| 四子王旗| 铜川市| 临澧县| 星子县| 洛南县| 延川县| 内江市| 天峻县| 锡林浩特市| 纳雍县| 江源县| 宣威市| 镇赉县| 新干县| 文山县| 方正县| 许昌县| 巴青县| 哈密市| 盘山县| 金华市| 尉氏县| 旺苍县| 特克斯县| 马山县| 威信县| 西林县| 巴中市| 谢通门县| 辽宁省| 曲阳县| 乌什县| 华蓥市| 微山县| 深泽县| 德钦县| 庐江县| 广宗县| 桐柏县| 历史|