2016-11-14 174 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