一 java 堆內(nèi)存和棧內(nèi)存
java把內(nèi)存分為兩種:一種是堆內(nèi)存,一種是棧內(nèi)存
堆: 堆內(nèi)存主要存儲(chǔ)實(shí)例化對(duì)象,數(shù)組挨措。由JVM動(dòng)態(tài)分配內(nèi)存空間。一個(gè)JVM只有一個(gè)堆內(nèi)存崩溪,線程是可以共享數(shù)據(jù)的。
棧:主要存儲(chǔ)局部變量和對(duì)象的引用變量斩松。每個(gè)線程都會(huì)有一個(gè)獨(dú)立的空間伶唯,所以線程之間的數(shù)據(jù)是不可以共享的。
在函數(shù)中定義的一些基本數(shù)據(jù)變量和對(duì)象的引用變量都會(huì)存儲(chǔ)到棧內(nèi)存中惧盹,當(dāng)在一段代碼中定義一段變量時(shí)乳幸,java就在棧內(nèi)存中為這個(gè)變量分配內(nèi)存空間,當(dāng)超過(guò)這個(gè)變量的作用域后钧椰,java會(huì)自動(dòng)釋放掉該變量分配的內(nèi)存空間粹断,該內(nèi)存的空間可以立即被另做它用。
堆內(nèi)存用于存放新建new對(duì)象和數(shù)組嫡霞。
在堆中分配內(nèi)存瓶埋,由java虛擬機(jī)自動(dòng)垃圾回收來(lái)處理。
二、堆和棧的共同點(diǎn)和優(yōu)缺點(diǎn)
1养筒、棧(stack)與堆(heap)都是java用于在Raw存放數(shù)據(jù)的地方曾撤,與C++不同,java自動(dòng)關(guān)了堆和棧晕粪,程序員不能設(shè)置堆和棧挤悉。
2、棧的優(yōu)勢(shì)是巫湘,存儲(chǔ)數(shù)據(jù)快装悲,僅次于cup中的寄存器,但缺點(diǎn)是尚氛,存在存在棧中的數(shù)據(jù)大小與生存期必須是確定的诀诊,缺乏靈活性,另外怠褐,棧內(nèi)存數(shù)據(jù)可以共享(這可能跟上面說(shuō)的有點(diǎn)沖突畏梆,上面剛說(shuō)的數(shù)據(jù)不能共享,現(xiàn)在又說(shuō)可以共享奈懒,很矛盾奠涌,但是,棧內(nèi)存在同一個(gè)線程中的數(shù)據(jù)是可以共享的磷杏,其他線程是不共享的)溜畅,堆內(nèi)存的優(yōu)勢(shì)是可以動(dòng)態(tài)的分配內(nèi)存的大小,生存期也不必要事先告訴編譯器极祸,java垃圾回收機(jī)制會(huì)自動(dòng)收走這些不在使用的數(shù)據(jù)慈格,但缺點(diǎn)是,由于運(yùn)行時(shí)是動(dòng)態(tài)分配內(nèi)存的遥金,所以存取速度較慢浴捆。
3、java中的數(shù)據(jù)類(lèi)型有兩種
一種是基本數(shù)據(jù)類(lèi)型(primitive types)稿械,共有8種选泻,即 int,short,long,byte,float,double,boolean, char(童鞋們要注意了,這里string不是基本類(lèi)型)這種類(lèi)型的定義是通過(guò)諸如 int a=3; long b =255L; 的形式來(lái)定義的美莫,稱為自動(dòng)變量页眯,值得注意的是,自動(dòng)變量存的是字面值厢呵,不是類(lèi)的實(shí)列窝撵,即不是類(lèi)的引用,這里并沒(méi)有類(lèi)的存在襟铭,如 int a = 3 碌奉;這里的a是一個(gè)指向int類(lèi)的引用短曾,指向3的字面值,這些字面值的數(shù)據(jù)道批,大小可知错英,生存期可知(這些字面值固定定義在某個(gè)程序模塊里面,程序塊退化后隆豹,字段值就消失了)椭岩,出于追求速度的就存在棧內(nèi)存中。
棧有個(gè)特殊的重要性璃赡,就是存在棧中的數(shù)據(jù)可以共享判哥。
假設(shè)我們同時(shí)定義
int a = 3; int b = 3;
編譯器先處理 int a =? 3;首先他會(huì)在棧中創(chuàng)建一個(gè)變量值為a的引用,然后在查找有沒(méi)有字面值為3的地址碉考,沒(méi)找到塌计,就開(kāi)辟一個(gè)存放3的字面值地址,然后將a指向3的地址侯谁,接著處理 int b = 3锌仅;在創(chuàng)建完b的引用變量后,由于在棧中已有有了3 的字面值墙贱,便將b直接指向3的地址热芹,這樣a和b同時(shí)指向了3的情況。
特別注意的是惨撇,這種字面值的引用與類(lèi)對(duì)象的引用不同伊脓,假設(shè)兩個(gè)對(duì)象的引用同時(shí)指向一個(gè)對(duì)象,如果一個(gè)對(duì)象引用變量修改了這個(gè)對(duì)象的內(nèi)部狀態(tài)魁衙,那么另一個(gè)對(duì)象引用變量也會(huì)立刻反應(yīng)出這個(gè)變化报腔。相反,通過(guò)字面值的引用來(lái)修改其值剖淀,不會(huì)導(dǎo)致另外一個(gè)指向此字面值的引用的值也跟著改變的情況纯蛾。這就是上述情況,如果a的字面值更改成4后纵隔,b的字面值依然是3 茅撞,不會(huì)因?yàn)閍的字面值改變而改變。
另一種是包裝類(lèi)數(shù)據(jù)巨朦。如 Integer,String ,Double等將相應(yīng)的基本數(shù)據(jù)類(lèi)型包裝起來(lái)的類(lèi)。這些數(shù)據(jù)類(lèi)型全部存儲(chǔ)在堆內(nèi)存中剑令,java 用 new()語(yǔ)句來(lái)顯示的告訴編譯器糊啡,在運(yùn)行是才根據(jù)需要?jiǎng)討B(tài)創(chuàng)建,因此比較靈活吁津,缺點(diǎn)是占用更多的時(shí)間
三棚蓄、堆和棧的區(qū)別
1堕扶、各司其職
最主要的區(qū)別就是棧內(nèi)存用于存儲(chǔ)局部變量和方法的調(diào)用。而堆內(nèi)存用來(lái)存儲(chǔ)java中的對(duì)象梭依,無(wú)論是成員變量稍算,局部變量還是類(lèi)變量,他們指向的對(duì)象都存儲(chǔ)在堆內(nèi)存中役拴。
2糊探、獨(dú)有還是共享
棧內(nèi)存歸屬于單個(gè)線程,每個(gè)線程都會(huì)有一個(gè)棧內(nèi)存河闰,其存儲(chǔ)變量只能在所屬線程中可見(jiàn)科平、共享,即棧內(nèi)存可以理解為線程的私有內(nèi)存姜性。而堆內(nèi)存中的對(duì)象對(duì)所有線程可見(jiàn)瞪慧。堆內(nèi)存中的對(duì)象可以被所有線程訪問(wèn)。
3部念、異常錯(cuò)誤
如果棧內(nèi)存沒(méi)有可用空間存儲(chǔ)方法調(diào)用和局部變量弃酌,JVM會(huì)拋出Java.lang.StackOverFlowError
如果堆內(nèi)存沒(méi)有可用空間存儲(chǔ)生成的對(duì)象,JVM會(huì)拋出Java.lang.OutOfMemoryError.
4儡炼、空間大小
棧的內(nèi)存要原因小于堆內(nèi)存妓湘,如果你使用遞歸的話,那你的棧內(nèi)存會(huì)很快充滿射赛,如果遞歸沒(méi)有及時(shí)跳出多柑,很可能會(huì)發(fā)送StackOverFlowError問(wèn)題。
你可以通過(guò)-Xss選項(xiàng)設(shè)置棧內(nèi)存的大小楣责。-Xss選項(xiàng)可以設(shè)置堆開(kāi)始時(shí)的大小竣灌,-Xmx選項(xiàng)可以設(shè)置堆內(nèi)存的最大值。
這就是java中的堆和棧秆麸,理解好這個(gè)問(wèn)題初嘹,可以對(duì)你解決開(kāi)發(fā)中的問(wèn)題,分析堆和棧內(nèi)存使用沮趣,甚至性能調(diào)優(yōu)都有幫助屯烦。
四、java中的堆和棧
JVM是基于堆棧的虛擬機(jī)房铭,JVM為每個(gè)新創(chuàng)建的線程都分配了一個(gè)堆棧驻龟,也就是說(shuō),對(duì)于一個(gè)java程序員來(lái)說(shuō)缸匪。他的運(yùn)行就是通過(guò)對(duì)堆棧的操作來(lái)完成的翁狐。堆棧以幀為單位保持線程的狀態(tài),JVM對(duì)堆棧只進(jìn)行兩鐘操作:以幀為單位的壓棧和出棧操作凌蔬,我們知道露懒,某個(gè)線程正在執(zhí)行的方法稱為次線程的當(dāng)前方法闯冷,我們可能不知道,當(dāng)前方法使用的幀稱為當(dāng)前幀懈词。當(dāng)線程激活一個(gè)java方法蛇耀,JVM就會(huì)在java的堆棧中新壓入一個(gè)幀。這個(gè)幀自然稱為了當(dāng)前幀坎弯,在此方法執(zhí)行期間纺涤,這個(gè)幀將用來(lái)保存參數(shù),局部變量荞怒,中間計(jì)算過(guò)程和其他數(shù)據(jù)洒琢。這個(gè)這在這里和編譯原理中的活動(dòng)記錄的概念差不多的,從java的這種分配機(jī)制來(lái)看褐桌,堆棧又可以這樣理解衰抑,堆棧(stack)是操作系統(tǒng)在建立某個(gè)進(jìn)程時(shí)或者線程(在支持多線程操作系統(tǒng)中的線程)為這個(gè)線程建立的存儲(chǔ)區(qū)域。該區(qū)域具有先進(jìn)后出的特性荧嵌。
每一個(gè)java應(yīng)用都唯一對(duì)應(yīng)一個(gè)JVM實(shí)例呛踊,每一個(gè)實(shí)例唯一對(duì)應(yīng)一個(gè)堆。應(yīng)用程序在運(yùn)行中所創(chuàng)建的所有類(lèi)實(shí)例或數(shù)組都放在這個(gè)堆中啦撮,并由應(yīng)用所有的線程共享谭网,跟C/C++不同,java分配堆內(nèi)存是自動(dòng)初始化的赃春,Java中所有對(duì)象的 存儲(chǔ)空間都是在堆中分配的愉择,也就是說(shuō)在建立一個(gè)對(duì)象時(shí),在兩個(gè)地方都分配內(nèi)存织中,在堆中分配的內(nèi)存實(shí)際建立這個(gè)對(duì)象锥涕,而在堆中分配的內(nèi)存只有一個(gè)指向這個(gè)堆對(duì)象的指針(引用)而已。
Java把內(nèi)存劃分成兩種:一種是棧內(nèi)存狭吼,另一種是堆內(nèi)存层坠。在函數(shù)中定義的一些基本類(lèi)型的變量和對(duì)象的引用變量都在函數(shù)的棧內(nèi)存中分配,當(dāng)在一段代碼定義一個(gè)變量時(shí)刁笙,Java就在棧中為這個(gè)變量分配內(nèi)存空間破花,當(dāng)超過(guò)變量的作用域后,Java自動(dòng)釋放掉為該變量分配的內(nèi)存空間疲吸,該內(nèi)存空間可以立即另作它用座每。