?????????????????????????????? 內(nèi)存位置分析
存儲信息
java虛擬機(jī)棧:局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口信息。
堆:存放對象實(shí)例,幾乎所有的對象實(shí)例以及數(shù)組都在這里分配內(nèi)存,成員變量。
方法區(qū):存儲已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)(1.8后方法區(qū)改成了直接內(nèi)存中的元空間) 。
常量池:是方法區(qū)的一部分,主要有字面量(常量和字符串)和符號引用(類和接口的符號引用、字段的名稱和描述的符號引用、方法的名稱和描述的符號引用)。JDK1.7 及之后版本的 JVM 已經(jīng)將運(yùn)行時常量池從方法區(qū)中移了出來,在 Java 堆(Heap)中開辟了一塊區(qū)域存放運(yùn)行時常量池
對于java 和內(nèi)存之間,有如下幾點(diǎn)需要注意:
1.一個Java文件,只要有main入口方法,我們就認(rèn)為這是一個Java程序,可以單獨(dú)編譯運(yùn)行。
?2.無論是普通類型的變量還是引用類型的變量(俗稱實(shí)例),都可以作為局部變量,他們都可以出現(xiàn)在棧中。只不過普通類型的變量在棧中直接保存它所對應(yīng)的值,而引用類型的變量保存的是一個指向堆區(qū)的指針,通過這個指針,就可以找到這個實(shí)例在堆區(qū)對應(yīng)的對象。因此,普通類型變量只在棧區(qū)占用一塊內(nèi)存,而引用類型變量要在棧區(qū)和堆區(qū)各占一塊內(nèi)存。
3.棧中的數(shù)據(jù)和堆中的數(shù)據(jù)銷毀并不是同步的。方法一旦結(jié)束,棧中的局部變量立即銷毀,但是堆中對象不一定銷毀。因?yàn)榭赡苡衅渌兞恳仓赶蛄诉@個對象,直到棧中沒有變量指向堆中的對象時,它才銷毀,而且還不是馬上銷毀,需要一定的時間,且受各種因素如內(nèi)存堆的大小、處理器的速度等影響,所以垃圾回收器真正執(zhí)行是在啟動垃圾回收器后的某個時刻才能執(zhí)行。
4.以上的棧、堆、代碼段、數(shù)據(jù)段等等都是相對于應(yīng)用程序而言的。每一個應(yīng)用程序都對應(yīng)唯一的一個JVM實(shí)例,每一個JVM實(shí)例都有自己的內(nèi)存區(qū)域,互不影響。并且這些內(nèi)存區(qū)域是所有線程共享的。這里提到的棧和堆都是整體上的概念,這些堆棧還可以細(xì)分。
5 .類的成員變量在不同對象中各不相同,都有自己的存儲空間(成員變量在堆中的對象中)。而類的方法卻是該類的所有對象共享的,只有一套,對象使用方法的時候方法才被壓入棧,方法不使用則不占用內(nèi)存 。對于static變量,無論創(chuàng)建多少個對象(甚至沒有創(chuàng)建對象)都只占一份存儲空間。
6.對于靜態(tài)變量,Java運(yùn)行時系統(tǒng)在類裝載時為這個類的每個靜態(tài)變量分配一塊內(nèi)存,以后再生成該類的對象 。對于方法或者代碼塊中聲明的變量,編譯器不為其賦初始值,使用前必須為其賦其值。
7靜態(tài)變量是在類裝載時初始化的,因此在產(chǎn)生對象前就初始化了,這也就是可以使用類名訪問靜態(tài)變量的原因。.
8.初始化順序:
????? ①對于static變量,先初始化static變量和static初始化塊。
????? ②默認(rèn)值初始化實(shí)例變量(如果繼承父類,父類先初始化)
????? ③初始化實(shí)例變量和初始化塊
????? ④構(gòu)造方法初始化