【问题标题】:Steps in the memory allocation process for Java objectsJava 对象的内存分配过程中的步骤
【发布时间】:2010-09-24 04:10:06
【问题描述】:

当类实例化以下对象时,内存中会发生什么?

public class SomeObject{

    private String strSomeProperty;

    public SomeObject(String strSomeProperty){
        this.strSomeProperty = strSomeProperty;
    }
    public void setSomeProperty(String strSomeProperty){
        this.strSomeProperty = strSomeProperty;
    }
    public String getSomeProperty(){
        return this.strSomeProperty;
    }
}

在课堂SomeClass1:

SomeObject so1 = new SomeObject("some property value");

在课堂SomeClass2:

SomeObject so2 = new SomeObject("another property value");

如何为新实例化的对象及其属性分配内存?

【问题讨论】:

    标签: java object memory heap-memory


    【解决方案1】:

    让我们一步一步来:

    SomeObject so1 = new SomeObject("some property value");
    

    ... 实际上比看起来更复杂,因为您正在创建一个新字符串。可能更容易理解为:

    String tmp = new String("some property value");
    SomeObject so1 = new SomeObject(tmp);
    // Not that you would normally write it in this way.
    

    (绝对准确 - 这些并不完全等效。在原始版本中,“新字符串”是在编译时创建的,是 .class 图像的一部分。您可以将其视为性能黑客。)

    所以,首先 JVM 为字符串分配空间。您通常不知道或不关心 String 实现的内部结构,因此请相信一块内存正在用于表示“某些属性值”。此外,您还临时分配了一些内存,其中包含对字符串的引用。在第二种形式中,它被明确称为tmp;以您的原始形式,Java 处理它而不命名它。

    接下来,JVM 为新的 SomeObject 分配空间。这是 Java 内部簿记的一点空间,也是每个对象字段的空间。在这种情况下,只有一个字段,strSomeProperty

    请记住,strSomeProperty 只是对字符串的引用。目前,它将被初始化为 null。

    接下来,构造函数被执行。

    this.strSomeProperty = strSomeProperty;
    

    所有这一切就是将 reference 复制到字符串中,复制到您的 strSomeProperty 字段中。

    最后,为对象引用so1分配空间。这是通过对 SomeObject 的引用设置的。

    so2 的工作方式完全相同。

    【讨论】:

    • 不,它并不比看起来更复杂。 "a" 和 new String("a") 不是等价的表达式。字符串文字由编译器执行。它们在使用时不会产生额外的堆分配。
    • Touche :) 但是我认为实习在概念上比我描述的还要复杂。我想适当的答案取决于问题的意图。
    • 实习在概念上可能更复杂,但它的优点是正确。
    • 我将用化学进行类比。在英国的学校里,16 岁的孩子学习玻尔的原子模型。在很多情况下它是有用的,它比在更高层次上教授的真理更简单。不过,我将编辑文本以澄清。
    • 编辑失败。他没有创建一个新的字符串。您的第二个版本与第一个不同。
    【解决方案2】:

    Heinz M. Kabutz 博士的Determining Memory Usage in Java 给出了准确的答案,以及一个计算内存使用量的程序。相关部分:

    1. 类占用至少 8 个字节。所以,如果你说 new Object();您将在堆上分配 8 个字节。
    2. 每个数据成员占用 4 个字节,除了 long 和 double 占用 8 个字节。即使数据成员是一个字节,它仍然会占用4个字节!此外,使用的内存量以 8 字节块的形式增加。因此,如果您有一个包含 1 个字节的类,它将占用 8 个字节用于类和 8 个字节用于数据,总共 16 个字节(呻吟!)。
    3. 数组更聪明一点。基元被打包在数组中,所以如果你有一个字节数组,它们每个都会占用一个字节(哇!)。当然,内存使用量仍以 8 字节块的形式增加。

    正如人们在 cmets 中指出的那样,字符串是一种特殊情况,因为它们可以被实习。您可以以相同的方式推断它们占用的空间,但请记住,看起来同一个 String 的多个副本实际上可能指向同一个引用。

    【讨论】:

    • 请注意,这是 7 年的历史,并且基于当时在特定操作系统上使用特定 JVM 的经验证据(文章说了这么多)。除非您确定要在哪个 JVM 上运行,否则您不可能这么精确。
    • 好点。如果某些过去占用 4 个字节的东西在 64 位平台上占用 8 个字节,我不会感到惊讶。但是,程序根据经验确定对象的大小。在目标 VM 上运行它会给出准确的答案。
    • 也有可能较新的 VM 有更有效的方法来存储布尔值、短裤等,或者行为因平台而异(JME 可以针对内存进行优化),或者 JVM 将策略更改为它接近堆限制。
    • 数组如何更聪明?
    • @CraigP.Motlin 你的意思是存储 3 个整数需要 16 个字节吗?
    【解决方案3】:

    要记住的要点:

    1. 调用方法时,会在堆栈顶部创建一个框架。
    2. 一旦方法完成执行,控制流将返回到调用方法,并刷新其相应的堆栈帧。
    3. 在堆栈中创建局部变量。
    4. 实例变量在堆中创建并且是它们所属对象的一部分。
    5. 在堆栈中创建引用变量。

    参考:http://www.javatutorialhub.com/java-stack-heap.html

    【讨论】:

    • Local 引用变量在堆栈中创建。引用实例成员在包含对象的堆中创建。这些都不能回答问题。
    猜你喜欢
    • 1970-01-01
    • 2012-02-28
    • 2015-10-15
    • 2013-01-21
    • 1970-01-01
    • 1970-01-01
    • 2014-03-17
    • 1970-01-01
    • 2019-11-27
    相关资源
    最近更新 更多