溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何解決QT內(nèi)存泄漏的問題

發(fā)布時間:2021-09-24 14:36:54 來源:億速云 閱讀:960 作者:柒染 欄目:開發(fā)技術(shù)

如何解決QT內(nèi)存泄漏的問題,相信很多沒有經(jīng)驗(yàn)的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

    01前言

       前幾天,項(xiàng)目開展了一次代碼初次評審。會上,領(lǐng)導(dǎo)指出一些可能會帶來內(nèi)存泄漏的代碼,如下圖所示:

    如何解決QT內(nèi)存泄漏的問題

    圖中的pLayout在new的時候沒有指定任何父對象,且MainWindow的析構(gòu)函數(shù)中也沒有對pLayout做delete操作,這意味著為pLayout申請的內(nèi)存空間在程序運(yùn)行期間是一直沒有得到釋放的。實(shí)際上,項(xiàng)目代碼中,還有許多這種“隱患”:一個單例類的成員變量在new的時候沒有指定父對象、一個靜態(tài)類的成員在new的時候沒有指定父對象……

    這些“隱患”為何在目前程序運(yùn)行時沒有暴露出問題?基于這個疑惑,我研究了QT的半自動化的內(nèi)存管理,并結(jié)合實(shí)驗(yàn)進(jìn)行結(jié)果驗(yàn)證,現(xiàn)將分析過程記錄下來

    02 QT半自動化內(nèi)存管理要點(diǎn)

    • QObject及其派生類的對象,如果其parent非0,那么其parent析構(gòu)時會析構(gòu)該對象

    • QWidget及其派生類的對象,可以設(shè)置 Qt::WA_DeleteOnClose 標(biāo)志位(當(dāng)close時會析構(gòu)該對象)

    • QAbstractAnimation派生類的對象,可以設(shè)置 QAbstractAnimation::DeleteWhenStopped

    • QRunnable::setAutoDelete()、MediaSource::setAutoDelete()

    • 父子關(guān)系:父對象、子對象、父子關(guān)系。這是Qt中所特有的,與類的繼承關(guān)系無關(guān),傳遞參數(shù)與parent有關(guān)(基類、派生類,或父類、子類,這是對于派生體系來說的,與parent無關(guān))

    03實(shí)驗(yàn)過程詳解

    堆空間的內(nèi)存泄漏與改進(jìn)方法實(shí)踐實(shí)例

    繼承QWidget類的Test類,通過new為其分配內(nèi)存,沒有設(shè)置WA_DeleteOnclose屬性且使用完后也沒有delete

    如何解決QT內(nèi)存泄漏的問題

    如何解決QT內(nèi)存泄漏的問題

    如何解決QT內(nèi)存泄漏的問題

    實(shí)驗(yàn)結(jié)果:關(guān)閉testWidget窗口后,沒有調(diào)用其析構(gòu)函數(shù)打印“test delete”信息。直到程序結(jié)束之前testWidget的申請的內(nèi)存空間都未被釋放

    改進(jìn)方法A

    為testWidget窗口設(shè)置Qt::WA_DeleteOnClose屬性

    如何解決QT內(nèi)存泄漏的問題

    實(shí)驗(yàn)結(jié)果:關(guān)閉testWidget窗口后,調(diào)用了其析構(gòu)函數(shù),內(nèi)存被釋放了

    改進(jìn)方法B

    不設(shè)置Qt::WA_DeleteOnClose屬性,但是在new完testWidget后,程序退出前調(diào)用testWidget的delete函數(shù)

    如何解決QT內(nèi)存泄漏的問題

    如何解決QT內(nèi)存泄漏的問題

    實(shí)驗(yàn)結(jié)果:關(guān)閉testWidget窗口后,調(diào)用了其析構(gòu)函數(shù),內(nèi)存也被釋放了

    實(shí)驗(yàn)結(jié)論:QWidget及其派生類的對象,可以設(shè)置 Qt::WA_DeleteOnClose 標(biāo)志位(當(dāng)close時會析構(gòu)該對象)或者手動delete來釋放內(nèi)存

    ??臻g的內(nèi)存實(shí)踐實(shí)例不采用new,為testWidget分配??臻g,不設(shè)置Qt::WA_DeleteOnClose屬性,不手動delete

    如何解決QT內(nèi)存泄漏的問題

    如何解決QT內(nèi)存泄漏的問題

    實(shí)驗(yàn)結(jié)果:關(guān)閉窗口時調(diào)用了testWidget的析構(gòu)函數(shù),內(nèi)存被釋放了

    testWidget不采用new創(chuàng)建,直接將對象建議在??臻g,但設(shè)置Qt::WA_DeleteOnClose屬性或者使用后delete &testWidget

    如何解決QT內(nèi)存泄漏的問題

    如何解決QT內(nèi)存泄漏的問題

    實(shí)驗(yàn)結(jié)果:程序崩潰了,在第一次嘗試delete testWidget時就出錯

    如何解決QT內(nèi)存泄漏的問題

    實(shí)驗(yàn)結(jié)論:delete棧上分配的地址會出錯

    父對象和子對象析構(gòu)的實(shí)踐實(shí)例將testWidget的父對象設(shè)置為mainwindow

    如何解決QT內(nèi)存泄漏的問題

    實(shí)驗(yàn)結(jié)果:關(guān)閉窗口后,正常打印析構(gòu)信息。程序退出前,先釋放testWidget的空間,再釋放mainwindow的空間(兩者都是分配到棧空間)

    如何解決QT內(nèi)存泄漏的問題

    調(diào)整mainwindow和testWidget的構(gòu)造順序

    如何解決QT內(nèi)存泄漏的問題

    實(shí)驗(yàn)結(jié)果:程序崩潰

    分析原因:mainwindow析構(gòu)時會將其子對象testWidget也析構(gòu),但testWidget是分配到??臻g上的,delete棧上的空間會出錯。

    如何解決QT內(nèi)存泄漏的問題

    將testWidget分配到堆上,指定父對象為mainwindow,并在程序退出前delete mainwindow

    如何解決QT內(nèi)存泄漏的問題

    實(shí)驗(yàn)結(jié)果:delete mainwindow時,會將testWidget也一并delete

    如何解決QT內(nèi)存泄漏的問題

    將testWidget作為mainwindow的成員,但在構(gòu)造時不指定父對象,在main函數(shù)中delete mainwindow

    如何解決QT內(nèi)存泄漏的問題

    如何解決QT內(nèi)存泄漏的問題

    實(shí)驗(yàn)結(jié)果:應(yīng)用程序退出前只有mainwindow被析構(gòu)了

    如何解決QT內(nèi)存泄漏的問題

    實(shí)驗(yàn)結(jié)論:

    • 析構(gòu)函數(shù)總是出現(xiàn)在對象的生命期結(jié)束之時,靜態(tài)對象在程序運(yùn)行結(jié)束之時析構(gòu)。

    • 對象的構(gòu)造和析構(gòu)的關(guān)系是棧數(shù)據(jù)結(jié)構(gòu)中的入棧和出棧的關(guān)系。

    • 指定了父對象的子對象,在父對象被析構(gòu)時,會將其一并析構(gòu)掉

    Malloc分配的內(nèi)存空間實(shí)踐實(shí)例

    新建一個malloc_class,將malloc_class作為mainwindow的成員,在mallocClass的構(gòu)造函數(shù)里面用malloc申請500M內(nèi)存,在mainwindow放置一個button,點(diǎn)擊button就delete malloc_class,在任務(wù)管理器中看500M內(nèi)存是否有被釋放。對于malloc_class的new操作,測試了兩種情況:

    new malloc_class時不指定父對象new malloc_class時指定它的父對象為mainwindow

    如何解決QT內(nèi)存泄漏的問題

    如何解決QT內(nèi)存泄漏的問題

    實(shí)驗(yàn)結(jié)果:兩種情況一樣的結(jié)果,點(diǎn)擊button,在delete testWidget后,在testThread中申請的500M空間都沒有被釋放

    如何解決QT內(nèi)存泄漏的問題

    如何解決QT內(nèi)存泄漏的問題

    猜想是不是因?yàn)閙alloc的空間并沒有指定父對象,異想天開的又測試了另外一種情況:直接創(chuàng)建malloc_class時用malloc為其分配內(nèi)存,再在button的槽函數(shù)里delete malloc_class

    如何解決QT內(nèi)存泄漏的問題

    實(shí)驗(yàn)結(jié)果:程序崩潰了……

    如何解決QT內(nèi)存泄漏的問題

    查閱資料得知:malloc只能為POD類型數(shù)據(jù)(一個類或結(jié)構(gòu)體通過二進(jìn)制拷貝后還能保持?jǐn)?shù)據(jù)不變,具體解釋自查資料)分配內(nèi)存,其他的必須用new分配內(nèi)存。Malloc函數(shù)分配內(nèi)存空間時并不調(diào)用構(gòu)造函數(shù),同樣free函數(shù)再回收空間時也不調(diào)用析構(gòu)函數(shù)。

    實(shí)驗(yàn)結(jié)論:malloc分配的內(nèi)存空間都要自己管理,與QT的父子對象同步析構(gòu)沒有關(guān)系。也就是說應(yīng)該再次明確:指定父對象的并且基于QObject為基類的對象才會同步析構(gòu)

    注意:malloc的空間只是一個虛擬內(nèi)存,一定要初始化或者寫數(shù)據(jù)才會有物理內(nèi)存的體現(xiàn)

    對于在應(yīng)用程序中不是常駐的對象,應(yīng)習(xí)慣為其指定父對象,或著用完之后手動delete;對于應(yīng)用程序中常駐的對象,即便在應(yīng)用程序結(jié)束后操作系統(tǒng)會釋放其使用的內(nèi)存,也不建議隨性new沒有parent的對象??傊?,養(yǎng)成嚴(yán)格處理內(nèi)存分配和釋放內(nèi)存的好習(xí)慣,要清楚自己在編碼時使用了哪些內(nèi)存,什么時候需要釋放,不定時關(guān)注程序的內(nèi)存占用率。

    看完上述內(nèi)容,你們掌握如何解決QT內(nèi)存泄漏的問題的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    qt
    AI