簡體   English   中英

Java堆棧和堆內存管理

[英]Java stack and heap memory management

我想知道如何在以下程序中分配內存:

public class MemoryClass {

    public static void main(final String[] args) {
        int i = 0;
        MemoryClass memoryClass = new MemoryClass();
        memoryClass.myMethod(memoryClass);
    }

    private void myMethod(final Object obj) {
        int i = 1;
        String s = "HelloWorld!";

    }

}

現在,據我所知,下圖描述了內存分配的發生方式:
基本運行時內存分配


在上圖中,堆棧存儲器中的內存objs實際上是對它們放置在堆內存中的“ 實際對象 ”的引用。
以下是我想到的一系列問題:

  1. 當存儲S的方法呢?
  2. 如果我在myMethod創建了MemoryClass另一個對象,那么JVM會在堆棧內存中再次為相同的方法分配內存嗎?
  3. JVM在執行完成后是否會釋放分配給myMethod的內存,如果是這樣,它將如何管理問題2中提到的情況( 僅當JVM將內存多次分配給同一方法時才適用 )。
  4. 如果我只聲明了s並且沒有初始化它,那么JVM仍會為java.lang.String類的所有方法分配內存,如果是這樣,為什么呢?

存儲方法在哪里?

它們存儲在String類對象中; 它是在程序中首次引用String時由ClassLoader對象加載的對象。 我讀到這篇文章時所存在的JVM的所有實現都從未在類對象加載后釋放內存。 它在堆上。

如果我在myMethod中創建了MemoryClass的另一個對象,那么JVM會在堆棧內存中再次為相同的方法分配內存嗎?

不,對象的方法和數據是分開保存的,特別是因為JVM永遠不需要多個方法副本。

JVM在執行完成后是否會釋放分配給myMethod的內存,如果是這樣,它將如何管理問題2中提到的情況(僅當JVM將內存多次分配給同一方法時才適用)。

不,Java通常不會“立即釋放存儲在堆上的內容”。 這會使事情運行得太慢。 它只在垃圾收集器運行時釋放內存,並且只有當它運行垃圾收集器的算法決定它是時候時它才會這樣做。

如果我只聲明了s並且沒有初始化它,那么JVM仍會為java.lang.String類的所有方法分配內存,如果是這樣,為什么呢?

這取決於我認為的JVM實現,也許還有編譯器。 如果你聲明一個變量並且從不使用它,那么編譯器很可能(並且很常見)注意到它沒有用,也沒有把它放到類文件中。 如果它不在類文件中,則永遠不會被引用,因此它及其方法不會被加載等。如果編譯器無論如何都將它放入但是它從未被引用,那么ClassLoader將沒有任何理由加載它,但我是否會加載或不加載我有點模糊。 可能依賴於JVM實現; 它是否加載了東西,因為有類的變量或只有它們被引用? 有多少ClassLoader算法可以在4位數PIN碼頭上跳舞?

我鼓勵您閱讀有關JVM和ClassLoaders等內容; 你會通過閱讀它的工作原理來獲得更多,而不是用你能想到的例子來嘲笑它。

首先要做的事情是 :我假設你的問題在閱讀完這篇文章之后就出來了( 因為在那里我看到的圖表與你的圖表非常相似 )所以我不會引用或突出那里提到的任何要點並試圖回答用你在帖子中不那么明顯的要點提問。

閱讀所有問題,我的印象是你很清楚如何在堆棧和堆中分配內存,但對類的元數據有疑問,即在內存中的哪些位置,類的方法將被存儲以及如何回收它們。 那么,首先讓我嘗試解釋JVM內存區域:


JVM內存區域

讓我首先介紹這兩個描繪JVM內存區域的圖:

圖表來源

在此輸入圖像描述

圖表來源

在此輸入圖像描述

現在,從上面的圖表中可以清楚地看到JVM內存的樹形結構,我將嘗試對它進行修改@Adit:請注意,與您有關的區域是PermGen Space或非堆內存的永久生成空間 ) 。

  • 堆內存
    • 年輕一代
      • 伊甸園空間
      • 幸存者空間
    • 老一代
      • 終身代
  • NonHeap內存
    • 永久代
    • 代碼緩存( 我認為HotSpot Java VM“僅限”包含

堆內存

堆內存是運行時數據區,Java VM從中為所有類實例和數組分配內存。 堆可以是固定的或可變的大小。 垃圾收集器是一個自動內存管理系統,可回收對象的堆內存。

年輕一代

年輕代是創建所有新對象的地方。 當年輕一代被填滿時,進行垃圾收集。 此垃圾收集稱為Minor GC。 Young Generation分為以下兩部分

Eden space:最初為大多數對象分配內存的池。

幸存者空間:包含在伊甸園空間垃圾收集中幸存下來的對象的池。

老一代

舊生成內存包含多輪次要GC后長壽和存活的對象。 通常垃圾收集在Old Generation內存中完成時執行。 舊一代垃圾收集稱為主要GC,通常需要更長的時間。 老一代包含以下部分:

終身空間:包含在幸存者空間中存在一段時間的物體的池。

非堆內存

非堆內存包括在Java VM的內部處理或優化所需的所有線程和內存之間共享的方法區域。 它存儲每類結構,例如運行時常量池,字段和方法數據,以及方法和構造函數的代碼。 方法區域在邏輯上是堆的一部分,但是根據實現,Java VM可能不會垃圾收集或壓縮它。 與堆存儲器一樣,方法區域可以是固定的或可變的大小。 方法區域的內存不需要是連續的。

永久世代

該池包含虛擬機本身的所有反射數據,例如類和方法對象。 對於使用類數據共享的Java VM,這一代分為只讀和讀寫區域。

代碼緩存

HotSpot Java VM還包括代碼緩存,其中包含用於編譯和存儲本機代碼的內存。


特別回答OP的問題

存儲方法在哪里?

非堆內存 - >永久生成

如果我在myMethod中創建了MemoryClass的另一個對象,那么JVM會在堆棧內存中再次為相同的方法分配內存嗎?

堆棧內存只包含局部變量,因此新的MemoryClass ORV(對象引用變量)仍將在myMethod堆棧幀中創建,但JVM不會在“Permanent Generation”中再次加載MemoryClass所有方法,元數據等。

JVM只加載一次類,當它加載類時,則在該類的“永久生成”上分配空間,並且在JVM加載類時只發生一次。

JVM在執行完成后是否會釋放分配給myMethod的內存,如果是這樣,它將如何管理問題2中提到的情況(僅當JVM將內存多次分配給同一方法時才適用)。

myMethod創建的堆棧幀將從堆棧內存中刪除,因此將清除為局部變量創建的所有內存,但這並不意味着JVM將清除在“永久生成”中為您創建的對象創建的內存中分配的內存在myMethod

如果我只聲明了s並且沒有初始化它,那么JVM仍會為java.lang.String類的所有方法分配內存,如果是這樣,為什么呢?

特別是在談論String類時,JVM會過早地為“永久生成”方式為String分配空間,同時啟動JVM以及是否初始化String變量,從“永久生成”角度來看無關緊要。

談到其他用戶定義的類,JVM會在您定義類時加載類並在“永久生成”中分配內存,即使您不創建類的對象,內存也會在“永久生成”中分配( 非堆區域 ),當您創建類的對象時,內存將在“Eden Space”( 堆區域 )中分配。


以上信息和進一步閱讀的來源:

由於arsy接受的答案和hagrawal的答案很清楚,只想詳細說明第四個問題:

如果我只聲明了s並且沒有初始化它,那么JVM仍會為java.lang.String類的所有方法分配內存,如果是這樣,為什么呢?

基本上,雖然類數據(具有字段和方法信息)存儲在永久生成(從JDK-8開始的元空間)中,但重要的是要注意它在java.lang中的對象。字符串類(例如,包含該字符串的所有字符信息的char []),用於在堆上分配數據。

在創建新的字符串對象之前不會發生這種情況 - 使用'new'關鍵字或創建新的字符串文字(例如:“helloworld”)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM