<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          雜想之一個(gè)C++內(nèi)存泄露案例

          共 4217字,需瀏覽 9分鐘

           ·

          2022-07-05 13:23

          最近正好有個(gè)內(nèi)存泄露分析的案例,和大家分享下,其帶給我的一些思考。


          開(kāi)發(fā)模型與問(wèn)題發(fā)現(xiàn)的時(shí)機(jī)

          這些年來(lái)開(kāi)發(fā)模型從傳統(tǒng)的瀑布模型,逐步向敏捷開(kāi)發(fā)過(guò)渡。敏捷開(kāi)發(fā)將需求進(jìn)行細(xì)分后,進(jìn)行更快速的迭代,不斷的交付,從原先瀑布模型按半年,甚至幾年一次性交付,變成敏捷開(kāi)發(fā)模式的1個(gè)月,2周,甚至是幾天為一個(gè)交付周期。在這樣的開(kāi)發(fā)模式中,可以讓客戶更快速地使用功能給出反饋,開(kāi)發(fā)人員可以及時(shí)做出調(diào)整。但從開(kāi)發(fā)者的角度來(lái)看,在快速的迭代開(kāi)發(fā)中,CI/CD (持續(xù)集成/持續(xù)部署)成為不可或缺的部分,自動(dòng)化必須替代其中大部分的手動(dòng)工作。

          在瀑布模型時(shí)代的時(shí)候,當(dāng)開(kāi)發(fā)者開(kāi)發(fā)完成后,做完基本測(cè)試,交由測(cè)試人員進(jìn)行功能測(cè)試,性能測(cè)試等等。而此時(shí)給予測(cè)試人員測(cè)試和開(kāi)發(fā)人員修復(fù)問(wèn)題的時(shí)間,往往比較充足。但在敏捷開(kāi)發(fā)中,假設(shè)就以1個(gè)月為一個(gè)Sprint(沖刺), 那么假設(shè)開(kāi)發(fā)人員(RD)設(shè)計(jì)、開(kāi)發(fā)和單元測(cè)試時(shí)間為2~3周,1周的測(cè)試人員(QA)測(cè)試和開(kāi)發(fā)人員修復(fù)bug,剩余的時(shí)間測(cè)試人員進(jìn)行回歸測(cè)試,開(kāi)發(fā)人員準(zhǔn)備下一個(gè)Sprint。如果這個(gè)時(shí)候等到QA測(cè)試出一些比較難處理的棘手問(wèn)題(比如內(nèi)存泄露、內(nèi)存破壞等),那么對(duì)于項(xiàng)目的發(fā)布將會(huì)受到嚴(yán)重的挑戰(zhàn),時(shí)間比較短促了。

          然而不巧,本人這次碰到的內(nèi)存泄露案例,就在發(fā)布的前一周結(jié)束的時(shí)候發(fā)現(xiàn)的,此時(shí)對(duì)于即使有一定經(jīng)驗(yàn)的開(kāi)發(fā)人員無(wú)疑是很具有挑戰(zhàn)性的。

          那么在敏捷開(kāi)發(fā)中,讓問(wèn)題盡早地暴露顯得尤為重要,以下是個(gè)人的一些感想:

          1. 在研究(Research)階段,做好充分的學(xué)習(xí)和驗(yàn)證,要當(dāng)問(wèn)題追蹤(Trouble Shooting)一樣的細(xì)心,不放過(guò)任何的蛛絲馬跡,尤其是那些想當(dāng)然的場(chǎng)景,最后也許是讓你出乎意料的地方。要做到明確需求廣泛閱讀相關(guān)關(guān)資料與有相關(guān)經(jīng)驗(yàn)的人員謙虛溝通學(xué)習(xí)做代碼級(jí)別的驗(yàn)證,并且不放過(guò)蛛絲馬跡,還需要及時(shí)和相關(guān)組員匯報(bào)和討論相關(guān)研究進(jìn)展,發(fā)現(xiàn)可能的問(wèn)題和應(yīng)對(duì)方案

          2. 在設(shè)計(jì)過(guò)程中,要做到詳細(xì)設(shè)計(jì),盡量考慮到多種可能的場(chǎng)景,測(cè)試以及部署相關(guān)的。一般相關(guān)的人員(比如QA)或者預(yù)備人員,在參與會(huì)議過(guò)程中,要盡可能的認(rèn)真聆聽(tīng),盡量給出自己的建議和想法,讓設(shè)計(jì)方面的問(wèn)題及早地暴露。

          3. 開(kāi)發(fā)過(guò)程中,開(kāi)發(fā)人員要不斷的提高自己的編碼能力(比如熟知一些C++常見(jiàn)的避坑寫(xiě)法),做好單元測(cè)試,并且在編寫(xiě)代碼的時(shí)候,筆者也經(jīng)常有一些疑問(wèn)或者感覺(jué)某個(gè)地方未來(lái)可能會(huì)產(chǎn)生問(wèn)題的(根據(jù)墨菲定律,那一定會(huì)產(chǎn)生問(wèn)題),可以用筆記將其記錄下來(lái),或者本人比較喜歡用微軟的To Do(以前叫做Wunder List)去進(jìn)行記錄, 然后在有空閑時(shí)間的時(shí)候,對(duì)這些問(wèn)題進(jìn)行仔細(xì)查看和測(cè)試。

          4. 代碼Review。大家都知道代碼review的重要性,然而在項(xiàng)目進(jìn)行時(shí),卻往往沒(méi)有給代碼Review騰出足夠時(shí)間。那么在一些功能測(cè)試很難暴露的問(wèn)題,比如內(nèi)存泄露,內(nèi)存破壞,UAF(Use After Free, 使用釋放后的內(nèi)存)等問(wèn)題, 在代碼Review過(guò)程中也許會(huì)被發(fā)現(xiàn)。

          5. CI/CD中,集成自動(dòng)化的相關(guān)測(cè)試,比如單元測(cè)試(Unit Test),接口測(cè)試(Interface Test),集成測(cè)試(Integration Test),驗(yàn)收測(cè)試(User Acceptance Test), 以及壓力測(cè)試(Stress Test)。像本文所說(shuō)的內(nèi)存泄露問(wèn)題,應(yīng)該在持續(xù)的壓力測(cè)試過(guò)程中很可能會(huì)暴露出來(lái)。那這樣只要RD在開(kāi)發(fā)完代碼提交到代碼倉(cāng)庫(kù),觸發(fā)了CI/CD后,便可以自動(dòng)獲取測(cè)試報(bào)告,發(fā)現(xiàn)可疑的問(wèn)題。

          6. 如果不能在自動(dòng)化階段完成這些測(cè)試,那么在RD在進(jìn)行開(kāi)發(fā)的過(guò)程中,RD和QA可以共同準(zhǔn)備壓力測(cè)試的方法和腳本,便于在盡早的時(shí)候進(jìn)行測(cè)試,暴露問(wèn)題。


          內(nèi)存泄露的發(fā)現(xiàn)與分析方法

          在壓力測(cè)試中,盡可能時(shí)間長(zhǎng)地混雜不同的類型的樣例進(jìn)行測(cè)試。本文的案例主要發(fā)生在Windows平臺(tái), 可以使用Windows自帶的任務(wù)管理器性能監(jiān)視器或者sysinternals工具集中的Process Explorervmmap進(jìn)行觀察,首先區(qū)分出是常見(jiàn)的句柄泄露,還是堆內(nèi)存泄露。本文主要講的是堆內(nèi)存泄露

          當(dāng)確定程序有內(nèi)存泄露,然后又告訴你還有幾天就要發(fā)布了。這個(gè)時(shí)候不慌是不可能的,但是很有必要冷靜下來(lái)想一想該怎么做?

          第一步 不是用調(diào)試工具,而是先確定問(wèn)題發(fā)生范圍。那么先確認(rèn)是不是這個(gè)問(wèn)題在上一個(gè)發(fā)布版本已經(jīng)有了?

            • 如果上一個(gè)版本已經(jīng)有這個(gè)問(wèn)題了,并且跑了一段時(shí)間,并沒(méi)有引起太大的問(wèn)題。那么在綜合考慮后,也許你可以不用緊急的修復(fù)這個(gè)問(wèn)題,而是可以繼續(xù)發(fā)布,在下一個(gè)Sprint對(duì)問(wèn)題進(jìn)行查找,進(jìn)入第三步

            • 如果上一個(gè)版本沒(méi)有這個(gè)問(wèn)題,那么可以集中考慮這個(gè)Sprint編寫(xiě)的代碼的問(wèn)題,走進(jìn)第二步


          第二步 這個(gè)時(shí)候又要分以下幾種可能的情況:

            • 代碼量不大,那么可以和同事一起進(jìn)行所有的代碼Review,如果還找不出來(lái),進(jìn)入第三步

            • 代碼量比較大,那么這個(gè)時(shí)候除了和熟悉這部分代碼的同事一起Review代碼,還可以根據(jù)內(nèi)存泄露的速率和這次新增的業(yè)務(wù)邏輯,找到可疑的點(diǎn)。如果還是找不到,那么進(jìn)入第三步


          第三步 這個(gè)時(shí)候需要調(diào)試工具的介入。之前本人寫(xiě)過(guò)幾篇內(nèi)存泄露分析的方法:

            • <<微軟Debug CRT庫(kù)是如何追蹤C(jī)++內(nèi)存泄露的?>>: 這種做法只適合于小型的項(xiàng)目,而且對(duì)于第三方庫(kù)的內(nèi)存泄露無(wú)法進(jìn)行檢測(cè)。本文旨在通過(guò)分析微軟Debug CRT庫(kù)的實(shí)現(xiàn)的檢測(cè)內(nèi)存泄露的方式,從而闡述自我實(shí)現(xiàn)簡(jiǎn)易C++內(nèi)存泄露檢測(cè)的思想。

            • <<Windows程序內(nèi)存泄漏(Memory Leak)分析之UMDH>>: 這種方法有一定的局限:當(dāng)程序復(fù)雜,內(nèi)存頻繁的申請(qǐng)釋放,通過(guò)UMDH對(duì)比的文件將會(huì)非常的大,并且很難直接看出內(nèi)存泄露所在; 另外UMDH在收集信息的需要符號(hào)文件,不太適合于在客戶的機(jī)器上進(jìn)行操作。

            • <<Windows程序內(nèi)存泄漏(Memory Leak)分析之Windbg>>: 這種方法,需要分析者對(duì)Windbg和Windows的堆要比較熟悉,分析過(guò)程也相對(duì)比較麻煩,不是首選方法。

            • <<vmmap分析內(nèi)存泄露問(wèn)題>>: 雖然也可以用來(lái)做內(nèi)存泄露分析,但是一般本人喜歡用于做輔助分析,可以比較清晰的看出各種類型內(nèi)存的動(dòng)態(tài)變化。

            • <<Windows內(nèi)存泄露分析之DebugDialog>>: 這種是我目前分析內(nèi)存泄露問(wèn)題的首選方法,也是本案例中使用的主要方法, 其主要兩個(gè)步驟: 收集dump和自動(dòng)分析,從而找出可疑的內(nèi)存泄露對(duì)應(yīng)的函數(shù)調(diào)用棧。讀者可以跳轉(zhuǎn)到文章,查看詳細(xì)的信息,本文將精簡(jiǎn)原文所做的步驟和講解。


          當(dāng)本人在準(zhǔn)備對(duì)內(nèi)存泄露進(jìn)行分析的時(shí)候,便想到了之前寫(xiě)過(guò)的幾種方法,由于代碼比較復(fù)雜,也不太想消費(fèi)太多的腦力去回憶Windbg的種種指令(畢竟大多數(shù)時(shí)候,不需要用Windbg分析),綜合的考慮后選擇了DebugDialog

          第一步 打開(kāi)DebugDialog Collection,選擇你需要分析的問(wèn)題的類型,比如我們想要分析的是Native Memory and Handle Leak問(wèn)題, 然后選擇相應(yīng)的進(jìn)程:



          第二步 選擇你需要產(chǎn)生Dump的時(shí)間,最少要配置15分鐘,這個(gè)可以根據(jù)你項(xiàng)目產(chǎn)生Memory Leak的速度來(lái)決定。


          第三步 然后Active你配置的Rule,則需要監(jiān)測(cè)的進(jìn)程被注入LeakTrack.dll用于輔助分析。接下來(lái)靜心等待,直到產(chǎn)生了Dump文件。然后開(kāi)啟DebugDialog Analysis, 先配置好符號(hào)文件目錄:

          然后選擇MemoryAnalysis, 并且添加剛才Monitor后產(chǎn)生的Dump文件。點(diǎn)擊Start Analysis進(jìn)行分析。

          分析結(jié)束后,打開(kāi)報(bào)告, 直接拉到Leak Analysis部分:
          這一部分才是內(nèi)存泄露的關(guān)鍵部分,會(huì)列出詳細(xì)的內(nèi)存申請(qǐng)的位置和大小。首先注意查看的是Leak Probability 顯示為100%, 非常值得懷疑的部分,其列舉了申請(qǐng)內(nèi)存為4M的函數(shù)調(diào)用棧,可以根據(jù)函數(shù)調(diào)用棧(d:\test\test\memoryleak\source.cpp @ 24 + a)尋找到內(nèi)存泄露的地方。

          大功告成,趕緊修復(fù)后出一個(gè)Debug Build試一試吧。

          也有可能很不幸的是,由于一些原因沒(méi)有分析出問(wèn)題原因呢?那這個(gè)時(shí)候也許還有一種可能,如果可行,這部分代碼暫時(shí)不進(jìn)行發(fā)布。天塌下來(lái)又如何,就當(dāng)被子蓋吧!


          內(nèi)存泄露的原因

          本次的案例實(shí)在是羞愧和讀者朋友們分享,因?yàn)楸救酥皩?xiě)過(guò)一篇<<你踩過(guò)幾種C++內(nèi)存泄露的坑?>>,這個(gè)案例的原因也就是那篇文章的第一個(gè)坑。其中加重字體寫(xiě)著"當(dāng)你構(gòu)建一個(gè)類的時(shí)候,寫(xiě)析構(gòu)函數(shù)一定要切記釋放類成員關(guān)聯(lián)的資源。". 不過(guò)還是容我解釋下,當(dāng)初在對(duì)一個(gè)class做修改的時(shí)候,正好發(fā)現(xiàn)有一個(gè)成員變量可以復(fù)用,而沒(méi)有將其改造為智能指針std::unique_ptr,在寫(xiě)的時(shí)候還提醒自己,析構(gòu)函數(shù)別忘記寫(xiě)delete哦。我們不應(yīng)心存僥幸:記得手動(dòng)釋放內(nèi)存;而是盡量用正確的方法,智能指針去避免這種可能的問(wèn)題發(fā)生,否則分析的代價(jià)比用智能指針可大了很多。

          class MemoryLeakClass{public:  MemoryLeakClass()   {     m_pObj = new XXX_ResourceClass;  }  void DoSomething()  {    m_pObj->DoSomething();  }  ~MemoryLeakClass()  {    ;  }private:  XXX_ResourceClass* m_pObj;};


          總結(jié)

          紙上得來(lái)終覺(jué)淺,絕知此事要躬行。當(dāng)五花八門(mén)的方法和思路擺在面前,我們需要的先冷靜下來(lái),理清思路,然后再著手使用更合適的方法去解決問(wèn)題。當(dāng)然了,作為技術(shù)人員,在平時(shí)盡量做好技術(shù)積累的工作,比如本次案例中,本人之前寫(xiě)過(guò)的<<Windows內(nèi)存泄露分析之DebugDialog>>文章幫助我節(jié)省了很多的時(shí)間去重新回憶和整理。

          瀏覽 85
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久电影伊人 | 无码三级影院 | 成年人免费黄色视频网站 | 国产日韩三级 | 天堂一区二区三区18 |