java中內(nèi)存分為3塊分別為:棧、堆昏苏、方法區(qū)(實際上方法區(qū)存在在堆當中也可以說內(nèi)存分為棧尊沸、堆,但是方法區(qū)比較特殊所以單獨拿出來說)贤惯。
1)棧
1洼专、表示方法執(zhí)行的內(nèi)存模型,每一個方法被調(diào)用時都會創(chuàng)建一個棧幀(棧幀存儲在棧當中)孵构,棧幀存儲局部變量屁商,操作數(shù),方法出口等颈墅。
2蜡镶、jvm會為每一個線程都創(chuàng)建一個棧,用來存放該線程執(zhí)行方法的信息恤筛。
3官还、棧屬于線程私有的,不能實現(xiàn)兩個線程的共享
4毒坛、棧的存儲特性是先進后出望伦,后進先出林说!
5、棧是系統(tǒng)自動分配的屯伞,速度快述么!棧是一個連續(xù)的內(nèi)存空間!
6愕掏、存放基本類型的變量數(shù)據(jù)和對象的引用度秘,但對象本身不存放在棧中,而是存放在堆(new 出來的對象)或者常量池中(字符串常量對象存放在常量池中饵撑。)
看到這時我想到了一個問題:javaweb中假如從網(wǎng)頁上發(fā)起了2次請求剑梳,這是兩個線程,還是屬于一個線程滑潘?
上網(wǎng)詢問得知:同一用戶2次請求一個線程垢乙,不同用戶請求兩個線程。(同一個用戶可以理解為同一臺電腦)
2)堆
1语卤、堆用于存儲創(chuàng)建好的對象(數(shù)組也是對象)
2追逮、jvm中只有一個堆,被所有線程共享粹舵!
3钮孵、堆是一個不連續(xù)的內(nèi)存空間,分配靈活眼滤,速度慢巴席!、
3)方法區(qū)(靜態(tài)區(qū))
1诅需、jvm只有一個方法區(qū)漾唉,被所有線程共享!
2堰塌、方法區(qū)實際上也在堆中赵刑,只存儲類、常量相關的信息场刑。
3般此、用來存放程序中永遠不變或唯一的內(nèi)容。(類信息[java代碼]摇邦,class對象恤煞,靜態(tài)變量屎勘,字符串常量)
下面是一個關于java內(nèi)存的代碼
public static void main(String[] args) {
User u1=new User("小明");
User u2=new User("小明");
System.out.println(u1.getUserName()==u2.getUserName());
String a=new String("小明");
System.out.println(u1.getUserName()==a);
String b=new String();
b="小明";
System.out.println(a==b);
System.out.println(b==u1.getUserName());
}
輸出打印
true
false
false
true
原理:
1)String創(chuàng)建時施籍,如果是通過=賦值方式創(chuàng)建的話,就會先查詢常量池中是否存在該值概漱,如果不存在丑慎,在常量池中放入該值,并且棧中的變量引用常量池。
2)如果是new String("小明")這種方式創(chuàng)建的話竿裂,那么會將字符串存儲在堆中玉吁,棧中的變量引用堆。
public static void main(String[] args) {
String x = "ab";
change(x);
System.out.println(x);
}
public static void change(String x) {
x = "cd";
}
原理:
開始我看這段代碼時腻异,認為打印cd(實際輸出ab)进副,我犯了2個錯,
1悔常、我錯誤的認為形參x和變量x為同一個(實際上形參x和變量x是兩個)
2影斑、因為認為String類型為引用變量,那么String x和形參x引用同一個常量池的字符串(指向同一個引用)机打。那么形參x被改變矫户,那么String 相應的被改變。
以下詳細的解釋第二種錯誤:
String類型雖然為引用類型但是String類型比較特殊残邀。
String創(chuàng)建時皆辽,如果是通過=賦值方式創(chuàng)建的話,就會先查詢常量池中是否存在該值芥挣,如果存在直接引用常量池驱闷,如果不存在,在常量池中放入該值空免,并且棧中的變量引用常量池遗嗽。(這樣的String它在修改值時,實際上并沒有在原來的值上修改鼓蜒,而是相當于在創(chuàng)建[存在引用痹换,不存在常量池種創(chuàng)建再引用]),因此通過=賦值方式的String可以看作和基本類型一樣都弹,而通過new String("小明")這種方式創(chuàng)建的可以看作和對象一樣娇豫。
3、所以以上的代碼其實是這樣的畅厢,棧中存在了2個變量x,String x開始把內(nèi)存地址拷貝給了形參x(此時它們是一致的)冯痢,但運行到x="cd"時,它在常量池中創(chuàng)建了一個字符串cd,并且將內(nèi)存地址引用給了形參x框杜,那么此時形參x和String x并沒有引用同一個地址
(形參x引用常量池中的cd浦楣,而String x引用常量池中的ab)
而且 change(x);方法過后形參x的生命周期已過(被gc回收),實際上打印的是String x咪辱,當然是ab!
關于String使用連接符
String a="小明";
String b="小明";
String c=a+b;
String d="小明小明";
System.out.println(c==d);
1振劳、我錯誤的認為這里會輸出true,實際上輸出false油狂,錯誤理解如下
String a先在常量池中創(chuàng)建了"小明"历恐,那么a引用常量池中小明的地址寸癌。String b時發(fā)現(xiàn)常量池中有"小明",那么ba引用常量池中小明的地址弱贼,現(xiàn)在a==b為true蒸苇。(錯誤理解開始)String c發(fā)現(xiàn)"小明小明"常量池中沒有,那么在常量池中創(chuàng)建"小明小明"吮旅,c的引用指向常量池中的小明小明溪烤,String d發(fā)現(xiàn)發(fā)現(xiàn)"小明小明"常量池中存在,那么d的引用指向常量池中的小明小明庇勃。所以c==d為true氛什。
上網(wǎng)查詢網(wǎng)友,然后糾正了自己的錯誤理解匪凉,從開始錯誤的地方開始解釋:
String c=a+b;這行代碼并沒有在常量池當中創(chuàng)建枪眉,它是在堆的內(nèi)存中創(chuàng)建(+號連接符中有變量創(chuàng)建的String就在堆內(nèi)存中,而不在常量池中)再层。而d發(fā)現(xiàn)常量池中沒有"小明小明"那么創(chuàng)建贸铜,此時c和d的內(nèi)存地址當然不同,那么輸出false聂受。
如果將以上的代碼改為如下代碼:
final String a="小明";
String c=a+"小明";
String d="小明小明";
System.out.println(c==d);
用final修飾a變量的話會輸出true蒿秦,因為
final修飾后就是常量了(不可修改的)。當對這樣的字符串常量調(diào)用+運算的時候蛋济,jvm會在編譯的時候進行替換棍鳖,比如這兒 c = a +"明";因為a是final修飾的碗旅,所有會替換為小明,c就相當于c="小明"+“明”
那么String c會在常量池中創(chuàng)建渡处,那么c和的地址相同,那么c==d為true
基本類型的參數(shù)傳遞:
傳遞的是值的副本祟辟,副本的傳遞不會影響到原件医瘫。
引用類型參數(shù)的傳遞:
傳遞的是對象的地址,副本和原參數(shù)都指向了地址旧困。那么改變副本指向?qū)ο蟮闹荡挤荩敲丛瓍?shù)的值也發(fā)生了改變。
引用類型中String比較特殊(在上面已經(jīng)詳細說了:鹁摺)
還有引用類型中的八大類型的包裝類也比較特殊(一般情況下可以看作基本類型僚纷,但也不一樣)
對于包裝類說的很好的鏈接
https://www.cnblogs.com/dolphin0520/p/3780005.html
https://www.cnblogs.com/guodongdidi/p/6953217.html(Integer之間的比較)
對于存儲位置很好的鏈接
https://www.iteye.com/problems/96058
int[] a=new int[0];
一個長度為0的數(shù)組其實也在堆內(nèi)存中開辟了空間!
2019-01-09