Java堆躬贡、棧、方法區(qū)渡贾、常量池

1逗宜、Java棧

Java在函數(shù)中定義的基本類型(int,long,short,byte,float,double,boolean,char)的變量(局部變量和函數(shù)的形參)的引用和數(shù)據(jù),以及對象的引用都放在棧中存儲(chǔ)空骚。

1纺讲、棧的特點(diǎn)

  • 1、存取速度快囤屹。僅次于CPU中的寄存器熬甚。
  • 2、每個(gè)線程都會(huì)有一個(gè)椑呒幔空間乡括,不同棧之間不能直接訪問,所以線程之間不能共享?xiàng)V械臄?shù)據(jù)智厌。
  • 3诲泌、存在棧中的數(shù)據(jù)是可以共享的。

比如我們定義int a=3铣鹏;int b=3敷扫;a=4;
編譯器先處理int a=3诚卸;首先會(huì)在棧中創(chuàng)建一個(gè)變量a的引用葵第,然后在棧中查找有沒有字面值為3的地址绘迁,如果有則將a指向這個(gè)地址;如果沒有則開辟一塊內(nèi)存放字面值3卒密,然后將a引用指向這個(gè)地址缀台。
接著處理int b=3;哮奇,在棧中創(chuàng)建b的引用變量膛腐,由于在棧中字面值為3的地址已經(jīng)存在,所以直接將b指向這個(gè)地址屏镊。這樣a和b同時(shí)指向了字面值為3的這個(gè)地址依疼。
接著處理a=4;而芥,會(huì)在棧中查找字面值為4的地址,如沒有就開辟內(nèi)存存放字面值4膀值,讓a指向這個(gè)地址棍丐,如有就將a直接指向這個(gè)地址。
此時(shí)b依然等于3沧踏,不會(huì)等于4歌逢。這點(diǎn)和對象的引用不同,需要注意翘狱。

  • 4秘案、如果棧內(nèi)存中沒有足夠的空間可以使用,JVM會(huì)拋出java.lang.StackOverFlowError異常潦匈。
  • 5阱高、棧中定義的變量,在超出變量的作用域后茬缩,Java會(huì)自動(dòng)釋放為變量所分配的內(nèi)存空間赤惊,該內(nèi)存空間可以立即被另作他用。

2凰锡、堆

堆中主要用于存放new出來的對象和數(shù)組未舟。下面舉例看下對象的實(shí)例化過程。

Person a=new Person ("123")掂为;
Person b=new Person ("123")裕膀;

編譯器會(huì)先執(zhí)行Person a=new Person ("123");勇哗,會(huì)在堆中開辟內(nèi)存存放創(chuàng)建的對象Person ("123")昼扛,在棧中創(chuàng)建變量a,將a指向?qū)ο蟮膬?nèi)存首地址智绸,a就是該對象的引用野揪。
接著執(zhí)行Person b=new Person ("123")访忿;,雖然前面已經(jīng)創(chuàng)建了對象Person ("123")斯稳,但是只要使用關(guān)鍵字new海铆,就會(huì)在堆中開辟一塊新的內(nèi)存存放創(chuàng)建的對象,在棧中創(chuàng)建變量b挣惰,將b指向新對象的首地址卧斟。所以a和b指向并不是同一個(gè)對象。
如果定義Person c = a憎茂;a.setName("234")那么String name=c.getName()就等于“234”。
執(zhí)行Person c = a竖幔;則變量a和c都會(huì)指向同一個(gè)對象板乙,所以使用a變量修改對象中的內(nèi)容時(shí),c指向的對象的內(nèi)容也會(huì)改變拳氢。

2.1募逞、特點(diǎn)

  • 1、存取速度比棧中的慢馋评。
  • 2放接、一個(gè)JVM只有一個(gè)堆內(nèi)存,所以線程間是可以共享堆內(nèi)存中的數(shù)據(jù)的留特。
  • 3纠脾、如果堆中內(nèi)存不足,則會(huì)拋出java.lang.OutOfMemoryError異常蜕青。
  • 4苟蹈、定義在棧中的變量a指向堆中的對象Person的首地址,在超出變量的作用域后市咆,Java會(huì)自動(dòng)釋放a所分配的內(nèi)存空間汉操,此時(shí)就沒有引用指向?qū)ο罅耍菍ο蟛⒉粫?huì)馬上被回收蒙兰,需要等某個(gè)時(shí)間通過垃圾回收來回收內(nèi)存磷瘤。

3、常量池

常量池分為兩種:靜態(tài)常量池和運(yùn)行時(shí)常量池搜变。
靜態(tài)常量池
靜態(tài)常量池指的是在編譯期確定采缚,保存在class文件中的一些數(shù)據(jù)。常量池主要用于存放兩大類常量:字面量(Literal)和符號(hào)引用量挠他,字面量相當(dāng)于Java語言層面常量的概念扳抽,如文本字符串、聲明為final的常量值等,符號(hào)引用則屬于編譯原理方面的概念贸呢,包括了如下三種類型的常量:

  • 1镰烧、類和接口的全限定名;
  • 2楞陷、字段的名稱和描述符怔鳖;
  • 3、方法的名稱和描述符固蛾。
    運(yùn)行時(shí)常量池
    在運(yùn)行時(shí)常量池是方法區(qū)的一部分结执,在JDK1.7之后運(yùn)行時(shí)常量池從方法區(qū)中移出,放在堆中艾凯。
    常量池是為了避免頻繁的創(chuàng)建和銷毀對象而影響系統(tǒng)性能献幔,其實(shí)現(xiàn)了對象的共享。

3.1趾诗、字符串常量池

對于字符串蜡感,其對象的引用都是存儲(chǔ)在棧中的,如果是編譯期已經(jīng)創(chuàng)建好(直接用雙引號(hào)定義的)的就存儲(chǔ)在常量池中恃泪,如果是運(yùn)行期(new出來的)才能確定的就存儲(chǔ)在堆中铸敏。對于equals相等的字符串,在常量池中永遠(yuǎn)只有一份悟泵,在堆中有多份。

String s1 = "china";
String s2 = "china";
String s3 = "china";

String ss1 = new String("china");
String ss2 = new String("china");
String ss3 = new String("china");
s1==s2==s3闪水; //true
ss1!=ss2!=ss3!=s1!=s2!=ss3糕非; //true
ss1.eqauls(ss2);//true
ss1.eqauls(ss3)球榆;//true
ss1.eqauls(s1)朽肥;//true
ss1.eqauls(s2);//true
ss1.eqauls(s3)持钉;//true

image.png

可以看出s1衡招、s2、s3在編譯期就被創(chuàng)建每强,并存入到了常量池中始腾。編譯器在執(zhí)行String s1 = "china";時(shí)會(huì)先在常量池中查找是否存在字符串常量“china”,如果不存在就在常量池中new一個(gè)china字符串空执,存在就不new浪箭,然后讓棧中的變量指向這個(gè)china字符串。因此常量池中只有一個(gè)china字符串對象辨绊,然后在執(zhí)行String s2 = "china";String s3 = "china";時(shí)奶栖,會(huì)在常量池中找到china字符串,并讓s2、s3指向它宣鄙。
對于String ss1 = new String("china");在編譯時(shí)并不會(huì)創(chuàng)建袍镀,在運(yùn)行時(shí),通過new產(chǎn)生一個(gè)字符串(假設(shè)為“china”)時(shí)冻晤,會(huì)先去常量池中查找是否已經(jīng)存在字符串“china”苇羡,如果不存在則在常量池中創(chuàng)建一個(gè)“china”字符串對象,然后在堆中再創(chuàng)建一個(gè)常量池中的“china”對象的拷貝對象明也;如果常量池中存在宣虾,就直接在堆中創(chuàng)建一個(gè)常量池中此“china”對象的拷貝對象。

3.2温数、包裝類實(shí)現(xiàn)了常量池技術(shù)

對于8種基本數(shù)據(jù)類型大部分都有自己的包裝類绣硝,其中Byte,Short,Integer,Long,Character,Boolean都實(shí)現(xiàn)了常量池技術(shù),撑刺;而Byte,Short,Integer,Long類型在裝箱時(shí)會(huì)緩存了范圍[-128,127]的數(shù)據(jù)到數(shù)組中鹉胖,Character會(huì)緩存[0,127]范圍的數(shù)據(jù)到數(shù)組中進(jìn)行緩存。

  • 1够傍、對于Integer來說甫菠,范圍是[-128,127]的數(shù)在自動(dòng)裝箱時(shí)全部被自動(dòng)加入到了常量池里面,具體可查看Integer.valueof(int i)方法冕屯。
    Integer.valueOf()
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
    return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}

IntegerCache類

private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];

static {
    // high value may be configured by property
    int h = 127;
    String integerCacheHighPropValue =
        VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
    if (integerCacheHighPropValue != null) {
        try {
            int i = parseInt(integerCacheHighPropValue);
            i = Math.max(i, 127);
            // Maximum array size is Integer.MAX_VALUE
            h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
        } catch( NumberFormatException nfe) {
            // If the property cannot be parsed into an int, ignore it.
        }
    }
    high = h;

    cache = new Integer[(high - low) + 1];
    int j = low;
    for(int k = 0; k < cache.length; k++)
        cache[k] = new Integer(j++);

    // range [-128, 127] must be interned (JLS7 5.1.7)
    assert IntegerCache.high >= 127;
}

private IntegerCache() {}
}

實(shí)例

public class test {
    public static void main(String[] args) {
        Integer i1=10;
        Integer i2=10;
        System.out.println(i1==i2);
                System.out.println(i1.equals(i2));
    }
}
輸出:
true
true
  • 2寂诱、使用new關(guān)鍵字創(chuàng)建Integer時(shí),即使數(shù)據(jù)在范圍[-128,127]安聘,也不會(huì)去緩存中查找痰洒,直接在堆中創(chuàng)建一個(gè)新的Integer對象。并不會(huì)像new String("123")可能需要在堆中或常量池中各創(chuàng)建一個(gè)對象浴韭。
Integer i1=new Integer(10);
Integer i2=new Integer(10);
Integer i3=10;
System.out.println(i1==i2); //false
System.out.println(i1.equals(i2)); //true
System.out.println(i1==i3); //false
System.out.println(i1.equals(i3)); //true

-3丘喻、當(dāng)整數(shù)不在[-128,127]范圍內(nèi)時(shí),就會(huì)在堆中創(chuàng)建對象念颈。
看下面例子在內(nèi)存中的分配泉粉。

 public void test() {

    int a1 = 9; //自動(dòng)拆箱 Integer.intValue()
    int b1 = 9; //自動(dòng)拆箱 Integer.intValue()

    final int A2 = 9;
    final int B2 = 9;

    Integer a3 = new Integer(9);
    Integer b3 = new Integer(9);

    Integer a4 = 9; //自動(dòng)裝箱 調(diào)用Integer.valueOf(int)
    Integer b4 = 9; //自動(dòng)裝箱 調(diào)用Integer.valueOf(int)
    
}

如下圖


image.png

4、方法區(qū)

方法區(qū)也是各個(gè)線程共享的內(nèi)存區(qū)域榴芳,它用于存儲(chǔ)已經(jīng)被虛擬機(jī)加載的類信息嗡靡、常量、靜態(tài)變量翠语、即編譯器編譯后的代碼等數(shù)據(jù)叽躯。靜態(tài)變量、常量在方法區(qū),所有方法,包括靜態(tài)和非靜態(tài)的,也在方法區(qū)肌括。

5点骑、成員變量和局部變量在內(nèi)存中的分配

對于成員變量和局部變量:成員變量就是方法外部酣难,類的內(nèi)部定義的變量;局部變量就是方法或語句塊內(nèi)部定義的變量黑滴。局部變量必須初始化憨募。 形式參數(shù)是局部變量,局部變量的數(shù)據(jù)存在于棧內(nèi)存中袁辈。棧內(nèi)存中的局部變量隨著方法的消失而消失菜谣。 成員變量存儲(chǔ)在堆中的對象里面,由垃圾回收器負(fù)責(zé)回收晚缩。

class BirthDate {
    private int day;
    private int month;
    private int year;
 
    public BirthDate(int d, int m, int y) {
        day = d;
        month = m;
        year = y;
    }
    // 省略get,set方法………
}
 
public class Test {
    public static void main(String args[]) {
        int date = 9;
        Test test = new Test();
        test.change(date);
        BirthDate d1 = new BirthDate(7, 7, 1970);
    }
 
    public void change(int i) {
        i = 1234;
    }
}
image

對于以上這段代碼尾膊,date為局部變量,i,d,m,y都是形參為局部變量荞彼,day冈敛,month,year為成員變量鸣皂。下面分析一下代碼執(zhí)行時(shí)候的變化:

  1. main方法開始執(zhí)行:int date = 9; date局部變量抓谴,基礎(chǔ)類型,引用和值都存在棧中寞缝。
  2. Test test = new Test();test為對象引用癌压,存在棧中,對象(new Test())存在堆中荆陆。
  3. test.change(date); i為局部變量滩届,引用和值存在棧中。當(dāng)方法change執(zhí)行完成后被啼,i就會(huì)從棧中消失丐吓。
  4. BirthDate d1= new BirthDate(7,7,1970); d1為對象引用,存在棧中趟据,對象(new BirthDate())存在堆中,其中d术健,m汹碱,y為局部變量存儲(chǔ)在棧中,且它們的類型為基礎(chǔ)類型荞估,因此它們的數(shù)據(jù)也存儲(chǔ)在棧中咳促。day,month,year為成員變量,它們存儲(chǔ)在堆中(new BirthDate()里面)勘伺。當(dāng)BirthDate構(gòu)造方法執(zhí)行完之后跪腹,d,m,y將從棧中消失。
  5. main方法執(zhí)行完之后飞醉,date變量冲茸,test,d1引用將從棧中消失,new Test(), new BirthDate()將等待垃圾回收.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末轴术,一起剝皮案震驚了整個(gè)濱河市难衰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逗栽,老刑警劉巖盖袭,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異彼宠,居然都是意外死亡鳄虱,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門凭峡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拙已,“玉大人,你說我怎么就攤上這事想罕∮扑ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵按价,是天一觀的道長惭适。 經(jīng)常有香客問我,道長楼镐,這世上最難降的妖魔是什么癞志? 我笑而不...
    開封第一講書人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮框产,結(jié)果婚禮上凄杯,老公的妹妹穿的比我還像新娘。我一直安慰自己秉宿,他們只是感情好戒突,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著描睦,像睡著了一般膊存。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上忱叭,一...
    開封第一講書人閱讀 49,837評(píng)論 1 290
  • 那天隔崎,我揣著相機(jī)與錄音,去河邊找鬼韵丑。 笑死疙描,一個(gè)胖子當(dāng)著我的面吹牛藏姐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播侥衬,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬榮一對情侶失蹤铲掐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后值桩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體摆霉,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年奔坟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了携栋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡咳秉,死狀恐怖婉支,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情澜建,我是刑警寧澤向挖,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站炕舵,受9級(jí)特大地震影響何之,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜咽筋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一溶推、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奸攻,春花似錦蒜危、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至硝训,卻和暖如春占拍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捎迫。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留表牢,地道東北人窄绒。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像崔兴,于是被迫代替她去往敵國和親彰导。 傳聞我的和親對象是個(gè)殘疾皇子蛔翅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容