深入了解java虛擬機(jī)(1)-內(nèi)存方面的知識

一、java虛擬機(jī)內(nèi)存各個區(qū)域總結(jié)

java虛擬機(jī)在運(yùn)行時會把它管理的內(nèi)存分為幾個不同的數(shù)據(jù)區(qū)域康吵,基本上可以分成兩個部分,一個是由所有線程共享的數(shù)據(jù)區(qū)域,另外一個數(shù)據(jù)區(qū)域就是線程自身的數(shù)據(jù)區(qū)域幌陕。其中,共享的數(shù)據(jù)區(qū)域包括方法區(qū)和堆汽煮,線程自身的數(shù)據(jù)區(qū)域有程序計數(shù)器搏熄、虛擬機(jī)棧和本地方法棧棚唆。

image.png

程序計數(shù)器

程序計數(shù)器可以理解為當(dāng)前線程執(zhí)行字節(jié)碼的指示器,即可以記錄下一條要執(zhí)行的字節(jié)碼心例,此內(nèi)存區(qū)域是唯一一個在java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError的區(qū)域宵凌。

虛擬機(jī)棧

java虛擬機(jī)棧是用于存儲局部變量表、操作數(shù)棧止后、動態(tài)鏈接摆寄、方法出口等,這里的局部變量表存儲了編譯期可知的各種基本數(shù)據(jù)類型(boolean坯门、byte微饥、char、short古戴、int欠橘、float、long现恼、double)肃续、對象引用類型和returnAddress類型。這里的long和double類型的數(shù)據(jù)占用了2個局部變量空間(Slot)叉袍,其余的數(shù)據(jù)類型只占用1個始锚。在這個區(qū)域,會出現(xiàn)兩種異常情況:一是StackOverFlowError喳逛,這是當(dāng)線程請求的棧深度大于虛擬機(jī)所允許的深度所拋出的異常瞧捌,二是OutOfMemoryError,這是當(dāng)無法申請到足夠的內(nèi)存時會拋出的異常润文。

本地方法棧

本地方法棧和虛擬機(jī)棧是類似的姐呐,區(qū)別在于虛擬機(jī)棧是為虛擬機(jī)執(zhí)行java方法服務(wù)的,本地方法棧是為native方法服務(wù)的典蝌,對于sun hotspot虛擬機(jī)曙砂,就直接把本地方法棧和虛擬機(jī)棧合并到一起。

java堆

在虛擬機(jī)規(guī)范中骏掀,所有的對象實例以及數(shù)組都是在堆上分配的鸠澈,但是現(xiàn)在隨著jit編譯期的發(fā)展以及逃逸分析技術(shù)的成熟,這個規(guī)定也不是絕對的了截驮。java堆是垃圾收集器主要的收集區(qū)域笑陈,也就是我們平常gc的主要收集區(qū)域。

方法區(qū)

方法區(qū)一般用于存儲被虛擬機(jī)加載的類信息侧纯、常量新锈、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)眶熬。如果是HotSpot虛擬機(jī)妹笆,會把方法區(qū)稱為“永生代”,但是像HotSpot這種做法块请,就更容易導(dǎo)致內(nèi)存溢出的問題。在JDK1.7中拳缠,就把原本放在永生代的字符串常量池移出了墩新。

運(yùn)行時常量池

運(yùn)行時常量池是方法區(qū)的一部分,運(yùn)行時常量池對于Class文件常量池的一個重要特征是具備動態(tài)性窟坐,即常量不一定在編譯期就產(chǎn)生海渊,在運(yùn)行期間也可以將新的常量放入池中,例如String類的intern()方法哲鸳。

二臣疑、從jvm角度去理解equals和==的區(qū)別

(1)對于java基本類型(boolean、byte徙菠、char讯沈、short、int婿奔、float缺狠、long、double)萍摊,應(yīng)該使用“==”來比較挤茄,比較的是他們的值。對于復(fù)合類型冰木,使用“==”比較的是它們的在內(nèi)存中的存放地址穷劈,使用equals方法是比較對象在堆內(nèi)存的地址,但在一些諸如String片酝、Integer囚衔、Date類中把Object中的這個方法覆蓋了,作用被覆蓋為比較內(nèi)容是否相同雕沿。由于jdk1.7已經(jīng)把常量池從方法區(qū)移除來了,這里只總結(jié)jdk1.7之后的區(qū)別猴仑。(也會加上jdk6的比較审轮,但是筆者沒有去驗證,僅引用其他人的結(jié)論)
(2)在jdk6辽俗,字符串常量池是在方法區(qū)疾渣,也就是說,對于字符串常量崖飘,是在方法區(qū)分配的內(nèi)存榴捡,而在jdk7之后的版本,是在java堆分配的內(nèi)存朱浴,跟對象實例是在同一個地方分配的內(nèi)存吊圾。還有要記住一點(diǎn)达椰,通過雙引號定義的字符串是直接在字符串常量池產(chǎn)生的,而通過new Stirng()方法產(chǎn)生的字符串是在java堆分配空間的项乒。

(1)利用“==”比較雙引號定義的String類型
    /**
     * "=="對于復(fù)合類型是比較地址
     */
    public void method1(){
        String s1 = "hello";
        String s2 = "hello";
        System.out.println(s1 == s2);
    }
結(jié)果:true啰劲,因為s1和s2引用的是同一個字符串,地址是一樣的
(2)利用“equals”比較雙引號定義的String類型
    /**
     * "equals"本來是比較地址檀何,但是Sting重寫了蝇裤,變成比較值了
     */
    public void method2(){
        String s1 = "hello";
        String s2 = "hello";
        System.out.println(s1.equals( s2 ));
    }
結(jié)果:true,equals比較的是值频鉴,固兩者是一樣的栓辜。
(3)雙引號定義的字符串和new String()字符串的比較
    public void method3(){
        String s1 = "hello";
        String s2 = new String("hello");
        System.out.println(s1 == s2);
        System.out.println(s1.equals( s2 ));
    }
結(jié)果:false和true,我們重點(diǎn)來看這種情況垛孔。
1啃憎、對于第一句話,是先在棧中創(chuàng)建了一個String類型的對象引用變量s1似炎,然后檢查常量池是否有“hello”這個字符串了辛萍,
如果有,就讓s1指向“hello”羡藐,如果沒有贩毕,就先在常量池創(chuàng)建“hello”字符串,然后讓s1指向“hello”仆嗦。
2辉阶、對于第二句話,也是先在棧中創(chuàng)建了一個String類型的對象引用變量s2瘩扼,然后在堆內(nèi)存中創(chuàng)建一個對象(new string)谆甜,
同時,如果字符串常量池沒有該字符串集绰,也會在常量池生成一個“hello”字符串规辱。
在這里說明一下,對于jdk7栽燕,因為常量池已經(jīng)在堆內(nèi)存了罕袋,所以常量池不一定會放字符串本身,也可能是一個引用碍岔,引用堆內(nèi)存里面的字符串浴讯,
這點(diǎn),在下面的intern()方法會著重說明蔼啦。

上面第三點(diǎn)所分配的內(nèi)存示意圖

image.png

首先榆纽,先在棧創(chuàng)建了一個String類型的對象引用變量s1,然后檢查常量池是否有“hello”這個字符串了,現(xiàn)在沒有奈籽,然后就在常量池創(chuàng)建“hello”饥侵;接著,在棧創(chuàng)建了一個String類型的對象引用變量s2唠摹,在堆內(nèi)存中創(chuàng)建一個對象爆捞,該對象存的就是“hello”字符串,所以s1和s2的地址是不一樣的勾拉,但是值是一樣的煮甥,使用“==”來判斷地址的時候,就會輸出false藕赞,而使用equlas來判斷值的時候成肘,就會輸出true。

三斧蜕、從jvm角度去理解String類的intern()方法双霍。

首先,我們先要知道intern()方法的作用批销,intern()方法的目的在于復(fù)用字符串對象以節(jié)省內(nèi)存洒闸,我們可以簡單地理解為,對一個字符串使用intern()方法的時候均芽,它會去字符串常量池查找該字符串是否已經(jīng)存在丘逸,如果已經(jīng)存在,就會直接返回常量池中的該字符串掀宋。

image.png

我們可以直接看Stirng.intern()方法的api文檔的解釋深纲,上圖紅框部分的意思是,當(dāng)調(diào)用intern()方法的時候劲妙,如果常量池里已經(jīng)包含一個字符串湃鹊,并且該字符串等于調(diào)用intern()方法的字符串對象(兩者是通過equals方法來判斷是否相等的),就返回常量池中的字符串镣奋,否則币呵,就把該字符串對象加入到常量池中,并且返回該字符串對象的引用唆途,因此富雅,對于兩個字符串s和t,s.intern() == t.intern()當(dāng)且僅當(dāng)s.equals(t)為真肛搬。

jdk6的intern()方法詳解

String s = new String("1");  
String s2 = "1";  
s.intern();  
System.out.println(s == s2);  
  
String s3 = new String("1") + new String("1");  
String s4 = "11";  
s3.intern();  
System.out.println(s3 == s4);  

在jdk6中,上面的結(jié)果都是false毕贼,解釋如下:
先看前面四句話:
(1)s在堆內(nèi)存創(chuàng)建了一個“1”對象温赔,然后在字符串常量池也生成了一個“1”;
(2)s2發(fā)現(xiàn)字符串常量池已經(jīng)有“1”了鬼癣,然后就會直接指向“1”陶贼;
(3)s.intern()方法啤贩,會使得s的object對象指向常量池中的“1”
(4)所以,s和s2的地址是不一樣的拜秧,所以第一個輸出false痹屹。
再看后四句話:
(1)s3先在堆內(nèi)存創(chuàng)建一個“1”對象,然后發(fā)現(xiàn)在字符串常量池已經(jīng)有“1”了枉氮,就不會再次生成“1”志衍,此時常量池是沒有“11”字符串的。
(2)s4會在字符串常量池生成了一個“11”聊替,然后讓s4指向“11”
(3)s3.intern()方法楼肪,會使得s3的object對象指向常量池中的“11”
(4)所以,s3和s4的地址是也不一樣的惹悄,所以第二個輸出false春叫。

image.png

如果我們把intern()方法跟前面的代碼調(diào)換,在jdk6中泣港,返回的結(jié)果是一樣的暂殖,都是false,這個就不分析了,這里跟前面不同的一點(diǎn)在于当纱,調(diào)用s3.intern()方法時呛每,由于常量池沒有“11”字符串,就會在常量池創(chuàng)建“11”字符串惫东。

String s = new String("1");  
s.intern();  
String s2 = "1";  
System.out.println(s == s2);  
  
String s3 = new String("1") + new String("1");  
s3.intern();  
String s4 = "11";  
System.out.println(s3 == s4);  

結(jié)論:這里無論調(diào)用還是不調(diào)用intern()方法莉给,結(jié)果都是false,因為地址都是不一樣的廉沮,但是在jdk7就不一樣了颓遏。

jdk7的intern()方法詳解

String s = new String("1");  
String s2 = "1";  
s.intern();  
System.out.println(s == s2);  
  
String s3 = new String("1") + new String("1");  
String s4 = "11";  
s3.intern();  
System.out.println(s3 == s4);  

在jdk7中,上面的情況也是返回兩個false滞时。從圖片可以看出來叁幢,跟jdk6的情況基本上是一樣的,沒什么區(qū)別坪稽,這里就不注重解釋曼玩,關(guān)鍵是下面把intern()方法跟上面代碼調(diào)換一行的情況,就大大不同了窒百。


image.png

如果我們把intern()方法跟前面的代碼調(diào)換黍判,在jdk7中,返回的結(jié)果就不一樣了篙梢。

String s = new String("1");  
s.intern();  
String s2 = "1";  
System.out.println(s == s2);  
  
String s3 = new String("1") + new String("1");  
s3.intern();  
String s4 = "11";  
System.out.println(s3 == s4); 

此時顷帖,第一個會輸出false,但是,第二個會輸出true,為什么呢贬墩,我們從下圖來看看解釋榴嗅。
第一個輸出跟前面一樣,沒什么區(qū)別陶舞,我們重點(diǎn)看看第二個輸出true的情況:
(1)s3先在堆內(nèi)存創(chuàng)建一個“1”對象嗽测,然后發(fā)現(xiàn)在字符串常量池已經(jīng)有“1”了,就不會再次生成“1”
(2)調(diào)用s3.intern()方法肿孵,如果是jdk6,就會在字符串常量池創(chuàng)建一個“11”字符串了唠粥,但是,對于jdk7,由于常量池就在堆內(nèi)存颁井,此時可以直接使用堆內(nèi)存里已經(jīng)存在的“11”字符串厅贪,就是s3的object對象,也就是說雅宾,字符串常量池不會創(chuàng)建“11”字符串养涮,它只會創(chuàng)建一個引用,該引用就是s3的object對象眉抬。
(3)s4也是直接指向了該引用贯吓,不會在常量池創(chuàng)建“11”字符串。
(4)所以蜀变,s3和s4的地址是一樣的悄谐,所以輸出true。

image.png

結(jié)論:在jdk7中库北,字符串常量池已經(jīng)不需要時刻保存一份字符串了爬舰,相反的,它可以保存一份引用寒瓦,該引用指向堆內(nèi)存的一個字符串對象即可情屹。

對于java內(nèi)存方面的知識就介紹到這里了,筆者對java內(nèi)存的理解不深杂腰,上面所說如果有錯誤垃你,歡迎指出。

參考資料
https://www.cnblogs.com/fengbs/p/7029013.html
http://blog.csdn.net/seu_calvin/article/details/52089040
http://blog.csdn.net/seu_calvin/article/details/52291082

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末喂很,一起剝皮案震驚了整個濱河市惜颇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌少辣,老刑警劉巖凌摄,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異漓帅,居然都是意外死亡望伦,警方通過查閱死者的電腦和手機(jī)林说,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門煎殷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屯伞,“玉大人,你說我怎么就攤上這事豪直×右。” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵弓乙,是天一觀的道長末融。 經(jīng)常有香客問我,道長暇韧,這世上最難降的妖魔是什么勾习? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮懈玻,結(jié)果婚禮上巧婶,老公的妹妹穿的比我還像新娘。我一直安慰自己涂乌,他們只是感情好艺栈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著湾盒,像睡著了一般湿右。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上罚勾,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天毅人,我揣著相機(jī)與錄音,去河邊找鬼尖殃。 笑死丈莺,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的分衫。 我是一名探鬼主播场刑,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蚪战!你這毒婦竟也來了牵现?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤邀桑,失蹤者是張志新(化名)和其女友劉穎瞎疼,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體壁畸,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贼急,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年茅茂,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片太抓。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡空闲,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出走敌,到底是詐尸還是另有隱情碴倾,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布掉丽,位于F島的核電站跌榔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏捶障。R本人自食惡果不足惜僧须,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望项炼。 院中可真熱鬧担平,春花似錦、人聲如沸芥挣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽空免。三九已至空另,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蹋砚,已是汗流浹背扼菠。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工女气, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留射富,地道東北人。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓查刻,卻偏偏與公主長得像墨坚,于是被迫代替她去往敵國和親秧饮。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355

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