java的虛擬機內(nèi)存區(qū)域
運行時數(shù)據(jù)區(qū)
1.方法區(qū)(method area)
2.堆(heap)
3.虛擬機棧(VM Stack)
4.本地方法棧(native method stack)
5.程序計數(shù)器(program counter register)
程序計數(shù)器
這是運行區(qū)內(nèi)存里面占的比較少的一塊內(nèi)存拳昌。它是用來標記當(dāng)前虛擬機運行的字節(jié)碼的行號。字節(jié)碼解釋器就是通過改變這個計數(shù)器來選取下一條字節(jié)碼來執(zhí)行。
線程恢復(fù)跟這個也有關(guān)系吼具。當(dāng)線程恢復(fù)之后浆劲,這個計數(shù)器標識的行號就是這個線程斷開的位置記錄雨席,每一個線程獨自擁有一個計數(shù)器
如果虛擬機現(xiàn)在執(zhí)行的是java方法止邮,那么計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令位置温技,如果執(zhí)行的是native方法笛求,那么不記錄廊移。
本地方法:Java不是完美的,Java的不足除了體現(xiàn)在運行速度上要比傳統(tǒng)的C++慢許多之外探入,Java無法直接訪問到操作系統(tǒng)底層(如系統(tǒng)硬件等)狡孔,為此Java使用native方法來擴展Java程序的功能》渌裕 可以將native方法比作Java程序同C程序的接口苗膝,其實現(xiàn)步驟:
1、在Java中聲明native()方法植旧,然后編譯辱揭;
2离唐、用javah產(chǎn)生一個.h文件;
3问窃、寫一個.cpp文件實現(xiàn)native導(dǎo)出方法亥鬓,其中需要包含第二步產(chǎn)生的.h文件(注意其中又包含了JDK帶的jni.h文件);
4域庇、將第三步的.cpp文件編譯成動態(tài)鏈接庫文件嵌戈;
5、在Java中用System.loadLibrary()方法加載第四步產(chǎn)生的動態(tài)鏈接庫文件听皿,這個native()方法就可以在Java中被訪問了熟呛。
java虛擬機棧
java虛擬機棧是線程私有。用來描述java方法的執(zhí)行写穴。每個方法執(zhí)行惰拱,都會在棧里面生成一個棧幀,用于存儲局部變量啊送,操作數(shù)棧偿短,動態(tài)鏈接,方法出口等馋没。
局部變量表:存編譯期就可知的基本數(shù)據(jù)類型和對象引用(看作指針吧)昔逗。
局部變量表的大小在編譯期間就可以確定大小,并且在運行期間不會改變這一塊內(nèi)存的大小篷朵。
定義了兩個異常
1.stackoverflow:如果線程請求的棧深度大于了虛擬機的規(guī)定深度勾怒。
2.oom:如果虛擬機棧在動態(tài)擴展的時候,無法申請到足夠的內(nèi)存声旺。
本地方法棧
用來描述native方法的執(zhí)行笔链,與上著類似。java的本地方法我好像還是在這里第一次遇到唉腮猖。鉴扫。以后再深究。這塊內(nèi)存同樣定義了兩種異常澈缺。
java堆
這塊倒是在沒學(xué)習(xí)虛擬機之前就了解過坪创。這是運行數(shù)據(jù)內(nèi)存中最大的一塊內(nèi)存,同樣也是gc重點關(guān)注的地方姐赡。這塊就是用來存對象實例的莱预。線程共享!
可以通過-Xmx, -Xms來控制這一塊的內(nèi)存大小项滑,哈哈依沮,終于能理解當(dāng)初在windows下運行android studio為啥要加這個聲明才能建立工程了
同樣的,如果內(nèi)存不夠用可并且不能擴展,那么就會報oom悉抵。
方法區(qū)
我理解的是這一塊就是來存儲類信息的肩狂。包括類的靜態(tài)變量,常量姥饰,編譯后的代碼傻谁。同樣也是線程共享的。
在現(xiàn)在的虛擬機設(shè)計下列粪,方法區(qū)不等同于永久帶(我理解的是gc永遠不會去回收內(nèi)存的區(qū)域)
運行常量池
常量池時包含在方法區(qū)的审磁,用于存放編譯期間生成的各種數(shù)據(jù)(class文件的常量池)。但是這個常量池不是靜態(tài)的岂座,是動態(tài)的态蒂,是可以在運行的時候存入新的數(shù)據(jù)。根據(jù)這個特性费什,String.intern()方法運用的很多钾恢。
簡單補充:String.intern()方法,這個方法會先判斷常量池里面有沒有這個string的對象鸳址,如果有的話會直接返回這個對象的引用瘩蚪,而不會再去生成一個新的對象,所以字符串不可變這個特性也支持了這種做法稿黍,如果不存在才會去生成一個新的對象然后再返回引用疹瘦。String s = "s";和String s = new String("s");的區(qū)別在于第一種會直接把這個數(shù)據(jù)對象放入常量池兒第二種會像生成一個對象一樣把數(shù)據(jù)對象放入java堆中。所以盡量使用第一種巡球,并且字符串拼接也要使用StringBuilder或者StringBuffer言沐,避免+,因為這樣每+一次就會生成一個StringBuffer對象酣栈,造成大量的空間浪費险胰。
如果這個區(qū)域的內(nèi)存不夠的話也會造成oom。
java對象的創(chuàng)建
對象的創(chuàng)建在我們程序員的眼里就是簡單的new矿筝,但是這里說的是jvm內(nèi)部時如何實現(xiàn)的鸯乃。首先虛擬機遇到一個new指令之后,會去常量池里面(方法區(qū)里面的)看這個有沒有這種符號引用的類類型(類信息)跋涣,如果沒有的話要去加載類信息,如果有的話就去java堆劃出一塊內(nèi)存區(qū)域鸟悴。
接下來是對象的設(shè)置陈辱,這里要涉及對象頭。對象頭一般分為兩塊(數(shù)組對象時三塊)细诸,一塊用來存儲對象的元信息沛贪,hash碼等等一塊用來確定這個對象是哪個類型的對象。數(shù)組對象會多出一塊來存儲大小。
這在虛擬機的眼里已經(jīng)完成了獨享的創(chuàng)建利赋,但是還差對象的初始化哈水评,接下來就是對象的init
java對象的訪問
我們都知道對象實例在java堆,引用存儲在虛擬機棧媚送。要通過引用來操作對象實例中燥,得去訪問它
1.句柄訪問法:
這種方法相當(dāng)于是把對象實例和引用解耦了(解耦無處不在!)塘偎,堆里面有一個對象實例有一個句柄池來指向?qū)ο髮嵗蛯ο箢愋蛿?shù)據(jù)(方法區(qū)里面的類信息)的地址疗涉,然后引用時指向這個句柄池。也就是說引用通過句柄池來訪問對象實例吟秩。這樣做就是有解耦的好處啦咱扣!如果對象實例的地址變了,引用不用變涵防,只需要修改句柄池的數(shù)據(jù)就好了闹伪。
2.直接指針法:
這種方法就是直接引用指向?qū)ο髮嵗牡刂防病_@種方法訪問速度更快壮池!
未經(jīng)博主同意偏瓤,不得轉(zhuǎn)載該篇文章