为啥 Java 使用堆来分配内存? [复制]

Posted

技术标签:

【中文标题】为啥 Java 使用堆来分配内存? [复制]【英文标题】:Why does Java uses heap for memory allocation? [duplicate]为什么 Java 使用堆来分配内存? [复制] 【发布时间】:2011-02-16 18:33:44 【问题描述】:

我刚刚在一本 java 书中读到这个语句,说 java 中的对象驻留在堆上。 使用堆是因为它是存储数据和快速检索数据的最佳方式吗?

对于初学者来说,我只知道数据结构。我的意思是为什么不堆叠或其他什么?

【问题讨论】:

如果您正在阅读一本有关数据结构的书,请注意计算机科学中“堆”一词有两种常见含义。一种是树状数据结构,另一种是用于内存分配的通用空间。 Java 的堆是内存分配的通用空间。 我也一直在想这个。这很有趣,因为在我的第一个学士学位期间,堆中的堆栈一词仅在编程上下文中使用。那时我对数据结构一无所知。既然学过数据结构,就可以理解为什么栈是栈,但不明白为什么堆是堆。我可以在答案中看到谁研究过数据结构而谁没有:)。实际上,格雷格的上述评论最好地回答了恕我直言的问题。正如我所读到的:'the' heap 不一定是'a' heap。 en.wikipedia.org/wiki/Heap 【参考方案1】:

Java 可以将对象存储在堆栈上,前提是它进行转义分析,确定方法返回时非本地对象不保留对对象的引用。它不允许您声明对象存储在堆栈中。

如果 Java 确实允许对象显式在堆栈上,那么当方法返回时会发生什么?对任何非本地对象持有的本地对象的任何引用会发生什么?

Java 设计者可能已经决定某些引用可能是无效,如果取消引用会导致未定义的行为。就像 C/C++ 中的指针一样。 Java 设计者似乎竭尽全力避免未定义的行为。

Java 设计者可以指定对本地对象的所有引用都变为空。找到所有这些参考资料会很困难。并且它会导致难以找到由假定不为空的引用突然变为空的引用引起的错误。包含对象引用的不可变对象是不可能的。并且将引用设置为 null 的通知机制必须跨线程工作。这一切的成本将远远高于本地存储的优势。

语言设计者之一 James Gosling 具有 Lisp 背景,这种影响体现在对象处理只留给垃圾收集器的想法中,编译器或运行时环境优化对象处理(escape分析)如果可能的话。

【讨论】:

【参考方案2】:

它是底层计算模型的产物。操作系统的内存看起来像一个大的,大部分是连续的空间,可以通过地址读取和写入数据。操作系统允许进程通过使用内存地址和读/写操作来获取一块内存(一个大的连续空间,通常至少是几 K 的一页)并随意处理。

Java 堆建立在此之上,即对于程序员来说,它看起来就像一个大内存袋(当然不是,即垃圾收集通常会移动东西),他获得“地址”(实际上是引用,他们实际上不是地址)用于写入该内存空间的数据(对象)。这使您可以最大程度地灵活地在此基础上构建更专业的数据结构。

请记住,它对程序员来说就像一个“堆”,因为这可以让您获得必要的灵活性,但它不必像这样实现。它是由垃圾收集器管理的一块内存,它使用一堆数据结构来完成它的工作,你可以或不可以考虑堆的一部分,即它是由 JVM 使用和分配的内存,但通常在这种情况下,只有程序员可以访问的内存才被认为是“堆”。

【讨论】:

【参考方案3】:

因为 Java 使用垃圾收集器来回收内存,这与 C 和 C++ 不同。在这些语言中,将堆栈用于局部变量* 是有意义的。在 Java 中,没有(局部)变量超出范围的概念,只是不再引用它,这使得它有资格进行垃圾回收(这必然会在该点之后某个时间发生) .

* 为清楚起见,这并不意味着 C/C++ 中没有堆,或者您不能使用 malloc/new 系列来分配变量只能在本地范围内使用。

【讨论】:

C 和 C++ 也使用堆。它通过 malloc/free(C 和 C++)或 new/delete(仅限 C++)访问。顺便说一句,OP 并没有询问局部变量。 您在我发表评论后编辑了您的答案。另外,您修改后的回复仍然不合时宜。 OP 想知道为什么要将对象放在堆上(“为什么不放在堆栈上?”)。您的回答“因为 Java 使用垃圾收集器......”是完全错误的。 @Marcelo Cantos,你真的可以在 Java 中将对象放在堆栈上吗?此外,请注意帖子的标题:“为什么 Java 使用堆来分配内存?”。我试图回答这个问题,我仍然认为我给出了一个合理的解释。当然,请随意不同意:-)【参考方案4】:

因为 Java 中的对象通常比创建它们的作用域更长,因此为作用域创建的堆栈帧将不复存在。

相反,当创建对象的范围不再存在时,分配的堆空间不会自动释放。

【讨论】:

【参考方案5】:

堆仅由 vm 用于分配和释放内存块。要访问那里的对象,您可以使用对内存块的引用(该引用在堆栈中)。 jvm 不允许直接访问内存(如在 C/C++ 中)。

【讨论】:

【参考方案6】:

为什么不将对象存储在堆栈中?那么,当前正在执行的方法停止执行后,堆栈会发生什么?

【讨论】:

【参考方案7】:

堆栈的问题在于您只能删除最近添加的内容。这适用于局部变量,因为它们在您进入和退出函数时来来去去,但对于生命周期不遵循单个函数的任意数据来说就不那么好了。内存堆允许您随意添加和删除数据。

【讨论】:

这个答案在某种程度上很棒,因为它解释了为什么堆有时比堆栈更好,这听起来好像堆也需要是一个堆数据结构,从字面上看。这也很令人困惑,方式大致相同。 @unwind:答案很简单,没有什么令人困惑的地方。您觉得哪些部分令人困惑?

以上是关于为啥 Java 使用堆来分配内存? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥编译器分配的内存比需要的多? [复制]

为啥 free() 不释放所有分配的内存位置? [复制]

为啥尽管我在变量中使用 malloc 分配更多内存,但当我打印变量的大小时,它仍然显示更少的内存/字节? [复制]

为啥要在堆上而不是栈上分配内存? [复制]

为啥在 C 中为数组声明和指向数组声明的指针动态分配的内存不同? [复制]

为啥Java使用堆数据结构来存储对象? [复制]