淺談QT內(nèi)存泄漏
01前言
前幾天,項(xiàng)目開展了一次代碼初次評審。會上,領(lǐng)導(dǎo)指出一些可能會帶來內(nèi)存泄漏的代碼,如下圖所示:
圖中的pLayout在new的時(shí)候沒有指定任何父對象,且MainWindow的析構(gòu)函數(shù)中也沒有對pLayout做delete操作,這意味著為pLayout申請的內(nèi)存空間在程序運(yùn)行期間是一直沒有得到釋放的。實(shí)際上,項(xiàng)目代碼中,還有許多這種“隱患”:一個(gè)單例類的成員變量在new的時(shí)候沒有指定父對象、一個(gè)靜態(tài)類的成員在new的時(shí)候沒有指定父對象……
這些“隱患”為何在目前程序運(yùn)行時(shí)沒有暴露出問題?基于這個(gè)疑惑,我研究了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)時(shí)會析構(gòu)該對象
- QWidget及其派生類的對象,可以設(shè)置 Qt::WA_DeleteOnClose 標(biāo)志位(當(dāng)close時(shí)會析構(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
實(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屬性
實(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ù)
實(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時(shí)會析構(gòu)該對象)或者手動delete來釋放內(nèi)存
??臻g的內(nèi)存實(shí)踐實(shí)例不采用new,為testWidget分配??臻g,不設(shè)置Qt::WA_DeleteOnClose屬性,不手動delete
實(shí)驗(yàn)結(jié)果:關(guān)閉窗口時(shí)調(diào)用了testWidget的析構(gòu)函數(shù),內(nèi)存被釋放了
testWidget不采用new創(chuàng)建,直接將對象建議在棧空間,但設(shè)置Qt::WA_DeleteOnClose屬性或者使用后delete &testWidget
實(shí)驗(yàn)結(jié)果:程序崩潰了,在第一次嘗試delete testWidget時(shí)就出錯(cuò)
實(shí)驗(yàn)結(jié)論:delete棧上分配的地址會出錯(cuò)
父對象和子對象析構(gòu)的實(shí)踐實(shí)例將testWidget的父對象設(shè)置為mainwindow
實(shí)驗(yàn)結(jié)果:關(guān)閉窗口后,正常打印析構(gòu)信息。程序退出前,先釋放testWidget的空間,再釋放mainwindow的空間(兩者都是分配到??臻g)
調(diào)整mainwindow和testWidget的構(gòu)造順序
實(shí)驗(yàn)結(jié)果:程序崩潰
分析原因:mainwindow析構(gòu)時(shí)會將其子對象testWidget也析構(gòu),但testWidget是分配到??臻g上的,delete棧上的空間會出錯(cuò)。
將testWidget分配到堆上,指定父對象為mainwindow,并在程序退出前delete mainwindow
實(shí)驗(yàn)結(jié)果:delete mainwindow時(shí),會將testWidget也一并delete
將testWidget作為mainwindow的成員,但在構(gòu)造時(shí)不指定父對象,在main函數(shù)中delete mainwindow
實(shí)驗(yàn)結(jié)果:應(yīng)用程序退出前只有mainwindow被析構(gòu)了
實(shí)驗(yàn)結(jié)論:
- 析構(gòu)函數(shù)總是出現(xiàn)在對象的生命期結(jié)束之時(shí),靜態(tài)對象在程序運(yùn)行結(jié)束之時(shí)析構(gòu)。
- 對象的構(gòu)造和析構(gòu)的關(guān)系是棧數(shù)據(jù)結(jié)構(gòu)中的入棧和出棧的關(guān)系。
- 指定了父對象的子對象,在父對象被析構(gòu)時(shí),會將其一并析構(gòu)掉
Malloc分配的內(nèi)存空間實(shí)踐實(shí)例
新建一個(gè)malloc_class,將malloc_class作為mainwindow的成員,在mallocClass的構(gòu)造函數(shù)里面用malloc申請500M內(nèi)存,在mainwindow放置一個(gè)button,點(diǎn)擊button就delete malloc_class,在任務(wù)管理器中看500M內(nèi)存是否有被釋放。對于malloc_class的new操作,測試了兩種情況:
new malloc_class時(shí)不指定父對象new malloc_class時(shí)指定它的父對象為mainwindow
實(shí)驗(yàn)結(jié)果:兩種情況一樣的結(jié)果,點(diǎn)擊button,在delete testWidget后,在testThread中申請的500M空間都沒有被釋放
猜想是不是因?yàn)閙alloc的空間并沒有指定父對象,異想天開的又測試了另外一種情況:直接創(chuàng)建malloc_class時(shí)用malloc為其分配內(nèi)存,再在button的槽函數(shù)里delete malloc_class
實(shí)驗(yàn)結(jié)果:程序崩潰了……
查閱資料得知:malloc只能為POD類型數(shù)據(jù)(一個(gè)類或結(jié)構(gòu)體通過二進(jìn)制拷貝后還能保持?jǐn)?shù)據(jù)不變,具體解釋自查資料)分配內(nèi)存,其他的必須用new分配內(nèi)存。Malloc函數(shù)分配內(nèi)存空間時(shí)并不調(diào)用構(gòu)造函數(shù),同樣free函數(shù)再回收空間時(shí)也不調(diào)用析構(gòu)函數(shù)。
實(shí)驗(yàn)結(jié)論:malloc分配的內(nèi)存空間都要自己管理,與QT的父子對象同步析構(gòu)沒有關(guān)系。也就是說應(yīng)該再次明確:指定父對象的并且基于QObject為基類的對象才會同步析構(gòu)
注意:malloc的空間只是一個(gè)虛擬內(nèi)存,一定要初始化或者寫數(shù)據(jù)才會有物理內(nèi)存的體現(xiàn)
04總結(jié)
對于在應(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í)慣,要清楚自己在編碼時(shí)使用了哪些內(nèi)存,什么時(shí)候需要釋放,不定時(shí)關(guān)注程序的內(nèi)存占用率。
到此這篇關(guān)于淺談QT內(nèi)存泄漏的文章就介紹到這了,更多相關(guān)QT內(nèi)存泄漏內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解C++中虛析構(gòu)函數(shù)的作用及其原理分析
這篇文章主要介紹了C++中虛析構(gòu)函數(shù)的作用及其原理分析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04C++構(gòu)造函數(shù)深度學(xué)習(xí)
這篇文章主要為大家詳細(xì)介紹了C++構(gòu)造函數(shù),深度學(xué)習(xí)C++構(gòu)造函數(shù),感興趣的小伙伴們可以參考一下2016-08-08Qt實(shí)現(xiàn)生成指定范圍內(nèi)隨機(jī)數(shù)與隨機(jī)字符串
這篇文章主要為大家詳細(xì)介紹了如何利用Qt實(shí)現(xiàn)生成指定范圍內(nèi)隨機(jī)數(shù)與隨機(jī)字符串,文中的示例代碼簡潔易懂,感興趣的小伙伴可以自己動手嘗試一下2023-07-07C++模板以及實(shí)現(xiàn)vector實(shí)例詳解
模板是為了實(shí)現(xiàn)泛型編程,所謂泛型編程,就是指編寫與類型無關(guān)的代碼,下面這篇文章主要給大家介紹了關(guān)于C++模板以及實(shí)現(xiàn)vector的相關(guān)資料,需要的朋友可以參考下2021-11-11C語言實(shí)現(xiàn)靜態(tài)順序表的實(shí)例詳解
這篇文章主要介紹了C語言實(shí)現(xiàn)靜態(tài)順序表的實(shí)例詳解的相關(guān)資料,這里提供是幫助大家學(xué)習(xí)理解這部分內(nèi)容,需要的朋友可以參考下2017-08-08