android jni new/delete 和 new[]/delete[]
今天,簡(jiǎn)單講講android里再jni使用new時(shí)如何釋放內(nèi)存。
這個(gè)其實(shí)是和C++有關(guān)的知識(shí),不過(guò)jni編程時(shí)還是需要注意的。所以這里記錄一下。
new 和 delete 到底是什么?
如果找工作的同學(xué)看一些面試的書(shū),我相信都會(huì)遇到這樣的題:sizeof 不是函數(shù),然后舉出一堆的理由來(lái)證明 sizeof 不是函數(shù)。在這里,和 sizeof 類(lèi)似,new 和 delete 也不是函數(shù),它們都是 C++ 定義的關(guān)鍵字,通過(guò)特定的語(yǔ)法可以組成表達(dá)式。和 sizeof 不同的是,sizeof 在編譯時(shí)候就可以確定其返回值,new 和 delete 背后的機(jī)制則比較復(fù)雜。
繼續(xù)往下之前,請(qǐng)你想想你認(rèn)為 new 應(yīng)該要做些什么?也許你第一反應(yīng)是,new 不就和 C 語(yǔ)言中的 malloc 函數(shù)一樣嘛,就用來(lái)動(dòng)態(tài)申請(qǐng)空間的。你答對(duì)了一半,看看下面語(yǔ)句:
你就可以看出 new 和 malloc 還是有點(diǎn)不同的,malloc 申請(qǐng)完空間之后不會(huì)對(duì)內(nèi)存進(jìn)行必要的初始化,而 new 可以。所以?new?expression?背后要做的事情不是你想象的那么簡(jiǎn)單。在我用實(shí)例來(lái)解釋 new 背后的機(jī)制之前,你需要知道?operator new?和?operator delete?是什么玩意。
operator new 和 operator delete
這兩個(gè)其實(shí)是 C++ 語(yǔ)言標(biāo)準(zhǔn)庫(kù)的庫(kù)函數(shù),原型分別如下:
void *operator new(size_t); //allocate an object void *operator delete(void *); //free an objectvoid *operator new[](size_t); //allocate an array void *operator delete[](void *); //free an array后面兩個(gè)你可以先不看,后面再介紹。前面兩個(gè)均是 C++ 標(biāo)準(zhǔn)庫(kù)函數(shù),你可能會(huì)覺(jué)得這是函數(shù)嗎?請(qǐng)不要懷疑,這就是函數(shù)!C++ Primer?一書(shū)上說(shuō)這不是重載 new 和 delete 表達(dá)式(如?operator=?就是重載?=?操作符),因?yàn)?new 和 delete 是不允許重載的。但我還沒(méi)搞清楚為什么要用 operator new 和 operator delete 來(lái)命名,比較費(fèi)解。我們只要知道它們的意思就可以了,這兩個(gè)函數(shù)和 C 語(yǔ)言中的 malloc 和 free 函數(shù)有點(diǎn)像了,都是用來(lái)申請(qǐng)和釋放內(nèi)存的,并且 operator new 申請(qǐng)內(nèi)存之后不對(duì)內(nèi)存進(jìn)行初始化,直接返回申請(qǐng)內(nèi)存的指針。
我們可以直接在我們的程序中使用這幾個(gè)函數(shù)。
new 和 delete 背后機(jī)制
知道上面兩個(gè)函數(shù)之后,我們用一個(gè)實(shí)例來(lái)解釋 new 和 delete 背后的機(jī)制:
我們不用簡(jiǎn)單的 C++ 內(nèi)置類(lèi)型來(lái)舉例,使用復(fù)雜一點(diǎn)的類(lèi)類(lèi)型,定義一個(gè)類(lèi) A:
class A { public:A(int v) : var(v){fopen_s(&file, "test", "r");}~A(){fclose(file);}private:int var;FILE *file; };很簡(jiǎn)單,類(lèi) A 中有兩個(gè)私有成員,有一個(gè)構(gòu)造函數(shù)和一個(gè)析構(gòu)函數(shù),構(gòu)造函數(shù)中初始化私有變量 var 以及打開(kāi)一個(gè)文件,析構(gòu)函數(shù)關(guān)閉打開(kāi)的文件。
我們使用
class *pA = new A(10); 來(lái)創(chuàng)建一個(gè)類(lèi)的對(duì)象,返回其指針 pA。如下圖所示 new 背后完成的工作:
簡(jiǎn)單總結(jié)一下:
所有這三步,你都可以通過(guò)反匯編找到相應(yīng)的匯編代碼,在這里我就不列出了。
好了,那么 delete 都干了什么呢?還是接著上面的例子,如果這時(shí)想釋放掉申請(qǐng)的類(lèi)的對(duì)象怎么辦?當(dāng)然我們可以使用下面的語(yǔ)句來(lái)完成:
delete pA; delete 所做的事情如下圖所示:
delete 就做了兩件事情:
好了,解釋完了 new 和 delete 背后所做的事情了,是不是覺(jué)得也很簡(jiǎn)單?不就多了一個(gè)構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用嘛。
這里我簡(jiǎn)單講講,new其實(shí)是創(chuàng)建一個(gè)內(nèi)存空間,然后自動(dòng)初始化變量。delete其實(shí)就是銷(xiāo)毀創(chuàng)建的空間,然后刪除變量。
二、不要混用 new/delete 和 new[]/delete[]
在默認(rèn)情況下,也就是不存在 operator new 的重載時(shí),new一個(gè)自定義類(lèi)型 ClassA 的對(duì)象時(shí),C++ 會(huì)先調(diào)用 malloc 來(lái)申請(qǐng)一塊 sizeof(ClassA) 大小的內(nèi)存(操作系統(tǒng)會(huì)記錄這塊內(nèi)存的首地址與大小),然后調(diào)用 ClassA 的構(gòu)造函數(shù)在這塊內(nèi)存上初始化對(duì)象。此時(shí),new 關(guān)鍵字會(huì)返回 malloc 得到的地址。調(diào)用delete時(shí),會(huì)首先執(zhí)行 ClassA 的析構(gòu)函數(shù),再調(diào)用 free 釋放 malloc 得到的指針。
而new[]則稍微復(fù)雜一點(diǎn),當(dāng)你調(diào)用 new ClassA[nCount] 申請(qǐng)一個(gè)對(duì)象個(gè)數(shù)為 nCount 的 ClassA 數(shù)組時(shí),編譯器(MSVC)會(huì)調(diào)用 malloc 申請(qǐng)一塊大小為 sizeof(ClassA) * nCount + 4 的內(nèi)存,多出來(lái)的 4 bytes 被放在 new[] 關(guān)鍵字放在地址 ptr 的前面,其中記錄了數(shù)組中元素的個(gè)數(shù)。當(dāng)調(diào)用 delete[] 刪除數(shù)組時(shí),會(huì)根據(jù)數(shù)組首地址前 4 bytes 中記錄元素的個(gè)數(shù)來(lái)依次調(diào)用數(shù)組中對(duì)象的析構(gòu)函數(shù)(每次指針偏移 sizeof(ClassA) 大小),再調(diào)用 free 釋放指針 (ptr - 4)。
因此,混用 new/delete 和 new[]/delete[] 通常會(huì)導(dǎo)致內(nèi)存訪(fǎng)問(wèn)崩潰。然后這里用了“通常”,也就是說(shuō)在某些特定情況下,混用 new/delete 和 new[]/delete[] 是不會(huì)有任何影響的:
然而當(dāng)項(xiàng)目代碼一旦復(fù)雜起來(lái),要分清什么時(shí)候上面兩個(gè)條件能夠成立就不是那么輕松的事了,因此最好的方法就是無(wú)論何時(shí)何地都不要混用 new/delete 和 new[]/delete[]。
這里簡(jiǎn)單區(qū)分一下,new/delete用來(lái)創(chuàng)建和銷(xiāo)毀對(duì)象,new[]/delete[]用來(lái)創(chuàng)建和銷(xiāo)毀數(shù)組。
三、不要 delete “void” 指針
類(lèi)似于下面形式的代碼:
// Author :大便一籮筐 2016-04-03struct StructA {char cData; }void* pBuffer = new StructA[nSize]; // do something... delete pBuffer;可能寫(xiě)過(guò)類(lèi)似代碼的同學(xué)會(huì)覺(jué)得這種寫(xiě)法并沒(méi)有什么問(wèn)題,事實(shí)也是如此,它能夠正常工作,既不會(huì)產(chǎn)生內(nèi)存泄漏,也不會(huì)運(yùn)行報(bào)錯(cuò)。
但是,上面情況只能說(shuō)是一種幸運(yùn)的巧合,如果發(fā)生一些微小的改變,結(jié)果就會(huì)發(fā)生意想不到的變化:
// Author :大便一籮筐 2016-04-03struct StructA {string strData; }void* pBuffer = new StructA[nSize]; // do something... delete pBuffer;細(xì)心的同學(xué)可能已經(jīng)看出來(lái)了,由于 pBuffer 是 void 指針,delete pBuffer 時(shí),并不會(huì)調(diào)用 StructA 的析構(gòu)函數(shù),而這導(dǎo)致了 string 的析構(gòu)函數(shù)也沒(méi)有被調(diào)用,最終產(chǎn)生的結(jié)果就是 string 中的字符串緩沖泄漏。
有的同學(xué)可能會(huì)說(shuō),C++ 不是支持多態(tài)嘛,我把 StructA 的析構(gòu)函數(shù)定義成虛函數(shù)不就好了。然而不幸的是,作為 C++ 的內(nèi)建類(lèi)型,void 并沒(méi)有定義析構(gòu)函數(shù),因此寄希望于多態(tài)是行不通的。
還有的同學(xué)可能會(huì)說(shuō),如果想定義 void 類(lèi)型的內(nèi)存緩沖區(qū)怎么辦? ???—— 別忘記我們還有 malloc 和 free。
所以,任何時(shí)候都不要嘗試 delete void 指針。
android jni new/delete 和 new[]/delete[]就講完了。
就這么簡(jiǎn)單。
總結(jié)
以上是生活随笔為你收集整理的android jni new/delete 和 new[]/delete[]的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: android jni jstring
- 下一篇: android jni malloc和f