java內(nèi)存(棧,堆,方法區(qū))

java中內(nèi)存分為3塊分別為:棧、堆、方法區(qū)(實際上方法區(qū)存在在堆當(dāng)中也可以說內(nèi)存分為棧、堆,但是方法區(qū)比較特殊所以單獨拿出來說)。
1)棧
1、表示方法執(zhí)行的內(nèi)存模型,每一個方法被調(diào)用時都會創(chuàng)建一個棧幀(棧幀存儲在棧當(dāng)中),棧幀存儲局部變量,操作數(shù),方法出口等。
2、jvm會為每一個線程都創(chuàng)建一個棧,用來存放該線程執(zhí)行方法的信息。
3、棧屬于線程私有的,不能實現(xiàn)兩個線程的共享
4、棧的存儲特性是先進(jìn)后出,后進(jìn)先出!
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ū)實際上也在堆中,只存儲類、常量相關(guān)的信息。
3、用來存放程序中永遠(yuǎn)不變或唯一的內(nèi)容。(類信息[java代碼],class對象,靜態(tài)變量,字符串常量)

下面是一個關(guān)于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";
}

原理:
開始我看這段代碼時,認(rèn)為打印cd(實際輸出ab),我犯了2個錯,
1、我錯誤的認(rèn)為形參x和變量x為同一個(實際上形參x和變量x是兩個)
2、因為認(rèn)為String類型為引用變量,那么String x和形參x引用同一個常量池的字符串(指向同一個引用)。那么形參x被改變,那么String 相應(yīng)的被改變。
以下詳細(xì)的解釋第二種錯誤:
String類型雖然為引用類型但是String類型比較特殊。
String創(chuàng)建時,如果是通過=賦值方式創(chuàng)建的話,就會先查詢常量池中是否存在該值,如果存在直接引用常量池,如果不存在,在常量池中放入該值,并且棧中的變量引用常量池。(這樣的String它在修改值時,實際上并沒有在原來的值上修改,而是相當(dāng)于在創(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,當(dāng)然是ab!

關(guān)于String使用連接符

String a="小明";
String b="小明";
String c=a+b;
String d="小明小明";
System.out.println(c==d);

1、我錯誤的認(rèn)為這里會輸出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;這行代碼并沒有在常量池當(dāng)中創(chuàng)建,它是在堆的內(nèi)存中創(chuàng)建(+號連接符中有變量創(chuàng)建的String就在堆內(nèi)存中,而不在常量池中)。而d發(fā)現(xiàn)常量池中沒有"小明小明"那么創(chuàng)建,此時c和d的內(nèi)存地址當(dāng)然不同,那么輸出false。

如果將以上的代碼改為如下代碼:

 final String a="小明";
String c=a+"小明";
String d="小明小明";
System.out.println(c==d);

用final修飾a變量的話會輸出true,因為
final修飾后就是常量了(不可修改的)。當(dāng)對這樣的字符串常量調(diào)用+運算的時候,jvm會在編譯的時候進(jìn)行替換,比如這兒 c = a +"明";因為a是final修飾的,所有會替換為小明,c就相當(dāng)于c="小明"+“明”
那么String c會在常量池中創(chuàng)建,那么c和的地址相同,那么c==d為true

基本類型的參數(shù)傳遞:
傳遞的是值的副本,副本的傳遞不會影響到原件。
引用類型參數(shù)的傳遞:
傳遞的是對象的地址,副本和原參數(shù)都指向了地址。那么改變副本指向?qū)ο蟮闹?,那么原參?shù)的值也發(fā)生了改變。
引用類型中String比較特殊(在上面已經(jīng)詳細(xì)說了?。?br> 還有引用類型中的八大類型的包裝類也比較特殊(一般情況下可以看作基本類型,但也不一樣)
對于包裝類說的很好的鏈接
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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容