【问题标题】:Storage of object references inside a object in Java memory modelJava内存模型中对象内部对象引用的存储
【发布时间】:2015-04-28 05:07:49
【问题描述】:

在研究链表实现时,我需要澄清引用和对象如何存储在堆栈和堆中,以用于对象自身具有引用的这种场景,

public class MyLinkedList {

    private Node head;
    private int listCount;

    public MyLinkedList() {
        head = new Node("0");
        listCount = 0;
    }

    public void add(Object data) {
        Node nodeTemp = new Node(data);
        Node nodeCurr = head;

        while (nodeCurr.getNext() != null) {
            nodeCurr = nodeCurr.getNext();
        }

        nodeCurr.setNext(nodeTemp);
        listCount++;

    }
}

public class LinkedListMain {
    public static void main(String[] args) {
        MyLinkedList ls = new MyLinkedList();
        ls.add("1");
}

现在 MyLinkedList 对象由堆栈中的“ls”引用引用,而 MyLinkedList 它自身位于堆中。我明白了。

但是从 MyLinkedList 构造函数中,当我们创建由“头”引用引用的新节点时,那个“头”引用存储在哪里?我的疑问是,由于“节点头”在(属于)MyLinkedList 对象内部,“头”是否存储在堆栈中与“ls” 是一种 在 MyLinkedList 对象中

【问题讨论】:

  • head 是 Node 的引用。 Node是用户定义的类吗?

标签: java


【解决方案1】:

你需要了解的关于 Java 的两件事:

  1. 所有 Java 对象 在Java 堆中分配。全部。
  2. 在 Java 中,变量从不对象。绝不。但是变量可以是对象的引用。 (或者变量可以是像int 这样的原语,但它们也不是对象。)

这意味着您的main 方法在Java 堆上分配了一个MyLinkedList 对象,并将对该对象的引用 存储在名为ls 的变量中。 MyLinkedList 对象(它本身没有名称)可以在名为 @987654327 的本地字段中存储对 Node 对象(也将存储在 Java 堆上)的 reference @。

没有其他对象存储在任何对象的“内部”。只有对其他对象的引用存储在里面。

警告:虽然这个答案是正确的,但我关注的是 Java 语言 的工作原理,只要您无法区分,运行时就可以进行各种优化。例如,“Java 堆”不是算法意义上的堆,甚至通常与“C++ 堆”不同。 Java JIT 允许从类似堆栈的结构(younggen)甚至从当前堆栈(由于逃逸分析)分配 Java 对象。也就是说,当您只是学习语言时,这些实现细节并不相关。

【讨论】:

    【解决方案2】:

    在 main() 中分配的“MyLinkedList”对象被“ls”引用。 “ls”在堆栈上 - main 的局部变量。 这个“MyListedList”实例的成员在堆上。 在 add() 中,nodeTemp 和 nodeCurr 是堆栈变量,它们是对节点的引用。节点本身在堆上。 “head”是一个类成员变量,与listcount一起在堆上。

    在 Java 中,(非静态)类成员必须始终位于堆上。

    【讨论】:

    • 请问原始和引用静态类成员如何?
    • 静态类成员(原语)不在堆栈中。它们是在程序启动时分配的。我不知道它们是在堆上还是程序映像的一部分(就像它们在 C 中一样)——但这并不重要。理解对对象的引用和对象本身之间的区别很重要。引用可以具有值“null”,或者是对对象的引用。对象总是通过引用访问 - 因此是“静态对象 foo” - 引用本身(“foo”)的存储是静态的,但它引用的任何对象都将始终在堆中。
    • 此外,存储引用所需的内存量与整数的大小大致相同。根据您的机器,它通常是 4 或 8 个字节。对象的大小取决于其非静态成员(加上少量开销)。因此,具有 500 个 int 成员的对象将需要大约 4*500=2000 字节的内存。
    • 谢谢。只是为了更好地理解我在问,所以正如您在回答“在 Java 中,(非静态)类成员必须始终在堆上。”以及程序启动时分配的静态类成员(原语)(堆或程序映像)。我希望通过平均程序启动您将其称为类加载知道吗?那么静态类成员(对象引用)是否也与程序启动并存储在堆或程序映像中时一样分配?而不是在堆栈上?我知道“静态对象 foo”的对象引用总是在堆中。只需要知道静态类引用“foo”存储在哪里?
    • "foo" 将存储在与原始类成员相同的位置。我不确定Java,但我怀疑这些成员是在类加载时分配在堆上的。 (另见静态初始化块)。如果您有“静态对象 foo = new Something()”,则为“foo”(以及其余的静态)分配空间将在分配 Something() 之前发生。实验:让 Something() 构造函数检查(并打印)MyLinkedList 的所有静态成员。在 MyLinkedList 中添加“foo”和“foo2”作为第一个和最后一个静态成员 - 然后运行并观察排序
    【解决方案3】:

    所有实例变量和对象都存储在堆上。而局部变量在堆栈上。 head 将在堆上,ls 将在堆栈上。

    public class MyLinkedList {
    
        private Node head;
        private int listCount;
    

    headlistCount 都将在堆上。

    【讨论】:

    • 所以你所说的实例变量是指类成员,对吧?所以 head 和 listCount 都将在堆和局部变量上,用于方法 add、nodeTemp 和 nodeCurr 在堆栈中
    • 是的,在Java虚拟机内部,每个线程都被授予一个Java栈,其中包含其他线程无法访问的数据,包括该线程调用的每个方法的局部变量、参数和返回值.堆栈上的数据仅限于原始类型和对象引用。在 JVM 中,不可能将实际对象的图像放在堆栈上。所有对象都驻留在堆上。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-24
    • 1970-01-01
    相关资源
    最近更新 更多