要說(shuō)Java中的棧,堆,方法區(qū)和常量池就要提到HotSpot,HotSpot是Sun JDK 和 Open JDK中所帶的虛擬機(jī)。 (Sun JDK 和 Open JDK除了注釋不同,代碼實(shí)現(xiàn)基本上是一樣的)
以下說(shuō)的內(nèi)容都是圍繞HotSpot。
Stack(棧):分為VM Stack(虛擬機(jī)棧)和Native Method Stack(本地方法棧),不過(guò)HotSpot虛擬機(jī)直接把本地方法棧和虛擬機(jī)棧合二為一了。
虛擬機(jī)棧: 線程私有的, 描述的是Java方法執(zhí)行的內(nèi)存模型,方法調(diào)用的同時(shí)創(chuàng)建一個(gè)棧幀(儲(chǔ)存局部變量表,操作數(shù)棧,方法出口等等),每個(gè)方法的調(diào)用直到執(zhí)行完成對(duì)應(yīng)的是棧幀在虛擬機(jī)中入棧和出棧的過(guò)程。
--------棧中的數(shù)據(jù)可以共享:
int a = 3;
int b = 3;
編譯器先處理int a = 3;首先它會(huì)在棧中創(chuàng)建一個(gè)變量為a的引用,然后查找棧中是否有3這個(gè)值,如果沒(méi)找到,就將3存放 進(jìn)來(lái),然后將a指向3。接著 處理int b = 3;在創(chuàng)建完b的引用變量后,因?yàn)樵跅V幸呀?jīng)有3這個(gè)值,便將b直接指向3。這樣,就出現(xiàn)了a與b同時(shí)均指向3的情況。這時(shí),如果再令 a=4;那么編譯器會(huì)重新搜索棧中是否有4值,如果沒(méi)有,則將4存放進(jìn)來(lái),并令a指向4;如果已經(jīng)有了,則直接將a指向這個(gè)地址。因此a值的改變不 會(huì)影響到b的值。要注意這種數(shù)據(jù)的共享與兩個(gè)對(duì)象的引用同時(shí)指向一個(gè)對(duì)象的這種共享是不同的,因?yàn)檫@種情況a的修改并不會(huì)影響到b, 它是由編譯 器完成的,它有利于節(jié)省空間。而一個(gè)對(duì)象引用變量修改了這個(gè)對(duì)象的內(nèi)部狀態(tài),會(huì)影響到另一個(gè)對(duì)象引用變量。
包裝類數(shù)據(jù),如Integer, String, Double等將相應(yīng)的基本數(shù)據(jù)類型包裝起來(lái)的類。這些類數(shù)據(jù)全部存在于堆中,Java用new()語(yǔ)句來(lái)顯示地告訴編譯器,在運(yùn)行時(shí)才根據(jù)需要?jiǎng)討B(tài)創(chuàng)建,因此比較靈活,但缺點(diǎn)是要占用更多的時(shí)間。
==比較的是對(duì)象的地址,也就是是否是同一個(gè)對(duì)象;
equal比較的是對(duì)象的值。
int 變?yōu)镮nteger 時(shí) 如果值在-128~127之間 則不會(huì)創(chuàng)建新的integer對(duì)象 儲(chǔ)存常量池中,這么做的目的是提高效率----->記得在別的地方看過(guò)不知道對(duì)不對(duì)
Java代碼
public class Test {
public static void main(String[] args)
{ int a1=1;
int b1=1;
int c1=2;
int d1=a1 b1;
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
System.out.println(a1==b1); //true 結(jié)果1
System.out.println(c1==d1); //true 結(jié)果2
System.out.println(c==d); //true 結(jié)果3
System.out.println(e==f); //false 結(jié)果4
}
}
分析:結(jié)果1:a1==b1如上面所述,會(huì)在棧 中開辟存儲(chǔ)空間存放數(shù)據(jù)。
結(jié)果2:首先它會(huì)在棧 中創(chuàng)建一個(gè)變量為c1的引用,然后查找有沒(méi)有字面值為2的地址,沒(méi)找到,就開辟一個(gè)存放2這個(gè)字面值的地址,然后將c1指向2的地址,d1為兩個(gè)字面值相加也為2, 由于在棧中已經(jīng)有2這個(gè)字面值,便將d1直接指向2的地址。這樣,就出現(xiàn)了c1與d1同時(shí)均指向3的情況。
在分析下面結(jié)果以前讓我們先對(duì)Java自動(dòng)拆箱和裝箱做個(gè)了結(jié):在自動(dòng)裝箱時(shí),把int變成Integer的時(shí)候,是有規(guī)則的,當(dāng)你的int的值在-128-IntegerCache.high(127) 時(shí),返回的不是一個(gè)新new出來(lái)的Integer對(duì)象,而是一個(gè)已經(jīng)緩存在堆 中的Integer對(duì)象,(我們可以這樣理解,系統(tǒng)已經(jīng)把-128到127之 間的Integer緩存到一個(gè)Integer數(shù)組中去了,如果你要把一個(gè)int變成一個(gè)Integer對(duì)象,首先去緩存中找,找到的話直接返回引用給你就 行了,不必再新new一個(gè)),如果不在-128-IntegerCache.high(127) 時(shí)會(huì)返回一個(gè)新new出來(lái)的Integer對(duì)象。
結(jié)果3:由于3是在范圍內(nèi)所以是從緩存中取數(shù)據(jù)的,c和d指向同一個(gè)對(duì)象,結(jié)果為true;
結(jié)果4:由于321不是在范圍內(nèi)所以不是從緩存中取數(shù)據(jù)的而是單獨(dú)有new對(duì)象,e和f并沒(méi)有指向同一個(gè)對(duì)象,結(jié)果為false;
JAVA堆:
Java堆是被所有線程共享的一塊區(qū)域,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建 ,此內(nèi)存的唯一目的就是存放對(duì)象實(shí)例和數(shù)組 GC 管理的主要區(qū)域。
分為新生代(Eden Survivor Survivor8:1:1)和老年代
Java堆可以處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯連續(xù)即可
關(guān)于對(duì)象的創(chuàng)建請(qǐng)參考:http://note.youdao.com/yws/public/redirect/share?id=5177014ee5ad1ac3f0af9fdab3b011a3&type=false
關(guān)于GC回收請(qǐng)參考:http://note.youdao.com/yws/public/redirect/share?id=96928e82082b4dec8831e5099769172b&type=false
方法區(qū):
運(yùn)行時(shí)常量池:
字面量:如文本字符串,聲明為final的常量值等。
public stick final int i =3;
String s='abc';
符號(hào)引用:類和接口的全限定名
字段的名稱和描述符
方法的名稱和描述符
String是一個(gè)特殊的包裝類數(shù)據(jù)。即可以用String str = new String('abc');的形式來(lái)創(chuàng)建,也可以用String str = 'abc';的形式來(lái)創(chuàng)建
String str = 'abc'創(chuàng)建對(duì)象的過(guò)程
1 首先在常量池中查找是否存在內(nèi)容為'abc'字符串對(duì)象
2 如果不存在則在常量池中創(chuàng)建'abc',并讓str引用該對(duì)象
3 如果存在則直接讓str引用該對(duì)象
至 于'abc'是怎么保存,保存在哪?常量池屬于類信息的一部分,而類信息反映到JVM內(nèi)存模型中是對(duì)應(yīng)存在于JVM內(nèi)存模型的方法區(qū),也就是說(shuō)這個(gè)類信息 中的常量池概念是存在于在方法區(qū)中,而方法區(qū)是在JVM內(nèi)存模型中的堆中由JVM來(lái)分配的,所以'abc'可以說(shuō)存在于堆中(而有些資料,為了把方法區(qū)的 堆區(qū)別于JVM的堆,把方法區(qū)稱為棧)。一般這種情況下,'abc'在編譯時(shí)就被寫入字節(jié)碼中,所以class被加載時(shí),JVM就為'abc'在常量池中 分配內(nèi)存,所以和靜態(tài)區(qū)差不多。
4(2)String str = new String('abc')創(chuàng)建實(shí)例的過(guò)程
1 首先在堆中(不是常量池)創(chuàng)建一個(gè)指定的對(duì)象'abc',并讓str引用指向該對(duì)象
2 在字符串常量池中查看,是否存在內(nèi)容為'abc'字符串對(duì)象
3 若存在,則將new出來(lái)的字符串對(duì)象與字符串常量池中的對(duì)象聯(lián)系起來(lái)
4 若不存在,則在字符串常量池中創(chuàng)建一個(gè)內(nèi)容為'abc'的字符串對(duì)象,并將堆中的對(duì)象與之聯(lián)系起來(lái)
String str1 = 'abc'; String str2 = 'ab' 'c'; str1==str2是ture
是因?yàn)镾tring str2 = 'ab' 'c'會(huì)查找常量池中時(shí)候存在內(nèi)容為'abc'字符串對(duì)象,如存在則直接讓str2引用該對(duì)象,顯然String str1 = 'abc'的時(shí)候,上面說(shuō)了,會(huì)在常量池中創(chuàng)建'abc'對(duì)象,所以str1引用該對(duì)象,str2也引用該對(duì)象,所以str1==str2
String str1 = 'abc'; String str2 = 'ab'; String str3 = str2 'c'; str1==str3是false ---------//可參考Java編程思想第四版P284,285
是因?yàn)镾tring str3 = str2 'c'涉及到變量(不全是常量)的相加,所以會(huì)生成新的對(duì)象,其內(nèi)部實(shí)現(xiàn)是先new一個(gè)StringBuilder,然后 append(str2),append('c');然后讓str3引用toString()返回的對(duì)象
String s = new String(“abc”); 產(chǎn)生幾個(gè)對(duì)象?
一個(gè)或兩個(gè),如果常量池中原來(lái)沒(méi)有 ”abc”, 就是兩個(gè)(參考棧中的數(shù)據(jù)共享)。
聯(lián)系客服