2016-11-14 237 views
1

我已經學習Java近一年了,但在動態內存分配方面仍然感到困惑。java動態內存分配

問題1:任何人都可以詳細說明當根據我寫的步驟執行下面的代碼時,在內存中發生了什麼(請糾正我,如果我錯了)?越詳細越好。

問題2:如果我想深入瞭解JVM或Java內存,應該閱讀/訪問什麼樣的書/網站?

class Student { 
    private static int counter; 
    private String name; 
    private int age; 
    private String grade = "grade 1"; 

    Student(String _name, int _age) { 
    this.name = _name; 
    this.age = _age; 
    } 

    public static void main(String[] args){ 
    Student s = new Student("Emma", 6); 
    } 
} 
  1. Student.class文件獲取加載,靜態變量counter時初始化數據區。
  2. main()被調用,JVM爲堆棧中的本地變量s分配內存。
  3. JVM爲堆上的成員變量name,agegrade分配存儲,併爲存儲分配零。
  4. grade初始化爲"grade 1"
  5. 構造Student()被調用初始化新的實例:JVM對堆棧_name_age分配內存,它們初始化爲"Emma"6,那麼它們的值複製到成員變量nameage
  6. JVM將此新實例分配給s
+1

這似乎是一個相當準確的描述給我。有沒有你不確定的地方?你的第二個問題可能是Stack Overflow的問題。我們有責任避免在這裏推薦資源。 –

+0

確保您必須交換點**(1)**是**(2)**。在類加載器完成加載類* Student *之後,將初始化類的靜態成員。 – Sandro

+1

@Sandro該類必須先加載才能運行其任何方法。交換點(1)和(2)是不可能的。 –

回答

2

您有4個和5個故障。構造函數首先以一種或另一種形式調用super(),然後按文本順序調用所有初始化程序和匿名init塊,然後在調用super()之後調用它們自己的主體(如果有)。您也可以在錯誤的地方分配和初始化_name_age:它在構造函數被調用之前發生。有關詳細信息,請參閱JLS和JVM規範。

+1

另一個重要的事情是,這些步驟中的一些(或甚至全部)可能根本不會發生。你得到的唯一保證是程序與所有這些步驟發生的程序無法區分。 – biziclop

+0

@biziclop'super()'被調用,除非當前類是'java.lang.Object',不是。調用所有初始化器和匿名init塊已經包含了沒有的情況。 OP的構造函數不是空的,所以它的body也被執行。在OP的代碼中,關於分配和分配構造函數參數的部分也不是可選的。 JLS或JVM規範中沒有關於'as-if'規則的任何內容。你在想C嗎? – EJP

+0

很多JLS規則都是在as-if模式下制定的。這就是使得熱點優化成爲可能的原因。因此,對於初學者來說,整個'main()'方法可能被檢測爲沒有可觀察到的副作用,因此完全消除。即使不是,轉義分析可能會提示VM決定在堆棧上創建對象。等等。 – biziclop

0

一些注意事項;

在較新版本的Java中,不保證靜態字段在從未使用時將被初始化。

對象的空間一次全部初始化,而不是每個字段的基礎上。整個對象被清零(除了標題),而不僅僅是字段被保存的位置。即對象具有填充值,它將被清零,並且將8個字節的字段清零不會比單個長字段慢。

構造函數在對象中的任何字段被賦值之前被調用,無論代碼是如何寫入的。

第一次使用字符串字符串時,字符串和基礎字符[]或字節[]也被創建。 grade被初始化爲對該對象的引用。

正如@biziclop所指出的那樣(不僅在理論上),這些事情都不會發生,因爲整個代碼可以優化到什麼都沒有(並且JIT今天可以做到這一點) t do會阻止創建字符串字符串,因爲在代碼加熱到足以確定代碼不執行任何操作之前,這種情況很快就會發生。

注意:如果AOT來到Java 9,代碼可能會減少爲NO-OP。

+0

字符串文字對象是在類加載時創建的,而不是第一次使用。 – EJP