1. 程式人生 > >Java 中記憶體分配情況

Java 中記憶體分配情況

一、記憶體分配區域如下:

1. 記憶體分配時涉及的區域:

  • 暫存器:在程式中無法控制;
  • 棧:存放基本型別的資料和物件的引用,但是物件本身不存放在棧中,而是存放在堆中;
  • 堆:存放用new產生的資料;
  • 靜態域:存放在物件中用static定義的靜態成員;
  • 常量池: 存放常量。

2. 記憶體分配中的棧和堆

1. 棧

在函式中定義的一些基本型別的變數資料,還有物件的引用變數都在函式的棧記憶體中分配。當在一段程式碼中定義一個變數時,Java就在棧中為這個變數分配記憶體空間,當該變數退出該作用域後,java會自動釋放掉為該變數分配的記憶體空間。

棧記憶體,是java程式的執行區,是線上程建立時建立的。它的生命週期跟隨執行緒的生命週期,執行緒結束棧記憶體也就釋放,對於棧來說不存在垃圾回收問題,只要執行緒結束,該棧就結束了。

棧中的資料是以棧幀(stackframe)的格式存在的。棧幀是一個記憶體區塊,是一個數據集,是有關方法(method)和執行期資料的資料集。當一個方法A被呼叫時就會產生一個棧幀F1被壓入到棧中,A方法再呼叫B方法,就會產生棧幀F2被壓入棧,執行完畢後,先彈出F2,在彈出F1。

2. 堆

堆記憶體用來存放由關鍵字new建立的物件和陣列。在堆中分配的記憶體,由java虛擬機器自動垃圾收集器來管理。

在堆中建立一個物件後,還可以在棧中定義一個變數,讓這個變數的值等於物件在堆記憶體中的首地址,棧中的變數就是物件的引用,相當於java中的指標。當程式執行到物件所在的語句塊之外,物件佔據的記憶體不會自動釋放,在沒有引用變數指向它時,隨後一個不確定的時間被垃圾收集器回收掉。

3.常量池(constantpool)

常量池指的是在編譯期被確定,並被儲存在已編譯的.class檔案中的一些資料。除了包含程式碼中所定義的各種基本型別(int,long等)和物件型(string、陣列等)的常量值(final),還包含一些以文字形式出現的符號引用。

類和介面的全限定名;

欄位的名稱和描述符;

方法和名稱的描述符;

在程式執行時,常量池會儲存在MethodArea(方法區)中,而不是堆中。

一個java虛擬機器例項只存在一個堆記憶體,堆記憶體的大小是可以調節的,類載入器讀取了類檔案後,需要把類、方法、常變數(const修飾的變數)放到堆記憶體中,堆記憶體分為三部分:

1)PermanentSpace永久儲存區

永久儲存區是一個常駐記憶體區域,用於存放jdk自身所攜帶的ClassInterface的元資料。也就是說它儲存的是執行環境必需的類資訊,被裝載到此區域的資料不會被垃圾收集器回收,關閉java虛擬機器才會釋放此區域佔的記憶體。

2)YoungGeneration Space新生區

新生區是類的誕生、成長、消亡的區域,新生區又分兩部分:伊甸區(Edenspace)和倖存區(Survivorspace),所有的類都是在伊甸區被new出來的。倖存區有兩個:0區(survivor0 space)和1區(survivor1space)。當伊甸區的空間用完時,程式再建立物件,虛擬機器將對伊甸區進行垃圾回收,將伊甸區中的不再被引用的物件進行銷燬,然後將伊甸區中的剩餘物件移動到倖存0區,如果倖存0區也滿了,將對該區進行垃圾回收,然後移動到1區,如果1區也滿了,就會移動到老年區。

3)Tenuregeneration space老年區

老年區儲存從新生區帥選出來的java物件。

4.Java虛擬機器中為什麼分堆區,棧區?

1)從軟體的角度,棧區代表了處理邏輯,而堆代表了資料。分開,使得處理邏輯更為清晰,體現了模組化的思想。

2)虛擬機器堆、棧的分離,使得堆中的內容可以被多個虛擬機器棧共享(也可以理解為多個執行緒訪問同一個物件,因為虛擬機器棧是隨著執行緒的建立而建立的),這種共享的益處很多,一方面提供了一種有效的資料互動方式(如共享記憶體);另一個方面,堆中的共享常量和快取可被多有虛擬機器棧訪問,節省了空間。

5. 堆和棧的合作

堆是一個執行時資料區,類的物件從中分配空間,堆的優勢是可以動態地分配記憶體大小,生存期不必事先告訴編譯器,但是缺點是,由於在執行時動態分配記憶體,存取速度慢。

棧的優勢是存取速度比堆快,僅次於暫存器,缺點是棧中的資料大小與生存期必須是確定的,缺乏靈活性。棧中存放一些基本型別的變數資料和物件引用。

棧有一個重要特性,就是棧中的資料可以共享。

Int a =3;

int b =3;

編譯器先處理inta =3;首先會在棧中建立一個變數為a的引用,然後查詢棧中是否有3這個值,如果沒有,就將3存放進來,然後將a指向3。接著處理intb =3;在建立完b的引用變數後,因為在棧中已經有3這個值,便將b直接指向3。這樣,a與b均指向3。如果再有語句a=4;編譯器會重新搜尋棧中是否有4值,如果沒有,將4存放進來,並將a指向4,如果已經有了,直接將a指向這個地址。因此a的改變不會影響到b的值。

這種資料的共享與兩個物件的引用同時指向一個物件的共享是不同的,因為這種情況下a的修改不會影響到b,它是由編譯器完成的,有利於節省空間。而一個物件的引用變數修改了這個物件的內部狀態,會影響到另一個物件引用變數。

Java中,String是一個特殊的包裝類資料,有兩種建立方式:

String str = newString(“abc”);

String str = “abc”;

第一種用new建立物件,是存在堆中,沒呼叫一次會建立一個新的物件。

第二種是先在棧中建立一個對String類的物件引用變數str,然後通過符號引用去字串常量池裡查詢有沒有“abc”,如果沒有,將“abc”放進字串常量池,並讓str指向“abc”,如果有,直接讓str指向“abc”。

6. 執行時的資料區域

所有執行緒共享方法區和堆;

虛擬機器棧、本地方法棧和程式計數器是執行緒隔離的資料區。

1) 程式計數器(ProgramCounter Register)

程式計數器是一塊較小的記憶體空間,作用相當於當前執行緒所執行的位元組碼的行號指示器。在java虛擬機器概念裡,位元組碼直譯器通過改變這個計數器的值來選取下一條需要執行的位元組碼指令,很多基礎功能都需要依賴這個計數器來完成,如分支,迴圈,跳轉等。

每條執行緒都需要一個獨立的程式計數器,並且各條執行緒之間的計數器互不影響,能夠獨立儲存。

2) Java的虛擬機器棧VMStack

虛擬機器棧是類中方法的執行過程的記憶體模型,與程式計數器一樣,虛擬機器棧也是執行緒私有的,它的宣告週期與執行緒相同。虛擬機器棧描述的是java方法執行的記憶體模型:每個方法被執行的時候都會同時建立一個棧幀(stackframe)用於儲存區域性變量表、運算元棧、動態連結、方法出口等資訊。每個方法被呼叫直至執行完成的過程,就對應一個棧幀在虛擬機器中從入棧到出棧的過程。

棧幀是虛擬機器棧的棧元素。對於活動執行緒中棧頂的棧幀,成為當前棧幀,這個棧幀所關聯的方法稱為當前方法,正在執行的位元組碼指令都只針對當前棧幀進行操作。

通常把java記憶體分為堆記憶體(heap)和棧記憶體(stack),其中的棧就是虛擬機器棧,或者說是虛擬機器棧中的區域性變量表部分。

區域性變量表存放了編譯期可知的各種基本資料型別、物件引用、返回地址型別。

3) Java堆

堆是類例項和陣列的分配空間,是多有執行緒共享的記憶體區域。堆可以處於物理上不連續的記憶體空間中,只要邏輯上是連續的即可。

4) 方法區

方法區是虛擬機器啟動時建立,是多有執行緒共享的記憶體區域。用來儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。

這個區域的記憶體回收目標主要是針對常量池的回收和對型別的解除安裝。

5) 執行時常量池

執行時常量池(RuntimeConstantPool)是方法區的一部分。用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類載入後存放到方法區的執行時常量池中。

二、 Java中static、final、static final的區別

1. final:

final可以修飾:屬性,方法,類,區域性變數(方法中的變數)

final修飾的屬性的初始化可以在編譯期,也可以在執行期,初始化後不能被改變。

final修飾的屬性跟具體物件有關,在執行期初始化的final屬性,不同物件可以有不同的值。

final修飾的屬性表明是一個常數(建立後不能被修改)。

final修飾的方法表示該方法在子類中不能被重寫,final修飾的類表示該類不能被繼承。

對於基本型別資料,final會將值變為一個常數(建立後不能被修改);但是對於物件控制代碼(亦可稱作引用或者指標),final會將控制代碼變為一個常數(進行宣告時,必須將控制代碼初始化到一個具體的物件。而且不能再將控制代碼指向另一個物件。但是,物件的本身是可以修改的。這一限制也適用於陣列,陣列也屬於物件,陣列本身也是可以修改的。方法引數中的final控制代碼,意味著在該方法內部,我們不能改變引數控制代碼指向的實際東西,也就是說在方法內部不能給形參控制代碼再另外賦值)。

2.static:

static可以修飾:屬性,方法,程式碼段,內部類(靜態內部類或巢狀內部類)

static修飾的屬性的初始化在編譯期(類載入的時候),初始化後能改變。

static修飾的屬性所有物件都只有一個值。

static修飾的屬性強調它們只有一個。

static修飾的屬性、方法、程式碼段跟該類的具體物件無關,不建立物件也能呼叫static修飾的屬性、方法等

static和“this、super”勢不兩立,static跟具體物件無關,而this、super正好跟具體物件有關。

static不可以修飾區域性變數。

3.static final和final static:

static final和final static沒什麼區別,一般static寫在前面。

4.static final:

static修飾的屬性強調它們只有一個,final修飾的屬性表明是一個常數(建立後不能被修改)。static final修飾的屬性表示一旦給值,就不可修改,並且可以通過類名訪問。

static final也可以修飾方法,表示該方法不能重寫,可以在不new物件的情況下呼叫

 

瞭解更多歡迎進Q裙:781725521   一