我正在学习一些具有C ++背景的Java课程和对象课程。我想知道为什么我们不能选择要在堆栈内存上声明的对象?除了原始类型之外,为什么一切都必须在堆上?
这里有一些东西可以澄清我在问什么。
基本上,如果我们有:
Scanner input = new Scanner(System.in);
那为什么我们不能首先在堆栈上拥有它?
答案 0 :(得分:3)
原始Java设计(20世纪90年代中期)最强吸引力之一是简单。支持基于堆的对象是必不可少的,而基于堆栈的对象是一种优化。 Java并不孤单:许多语言采用这种方法(LISP,Haskell,JavaScript,Ruby等)。基于堆栈的分配确实发生在Java中,但仅作为内部优化技巧而不是用户可以控制的东西。
特别要记住,对象传递给函数的指针("传递给方法"在Java语言中的引用)如何被被调用者处理有本质区别:如果它是基于堆栈的,则不允许保留指针。仅这一点就会产生巨大的复杂性和错误机会。
最后,基于堆栈的对象对垃圾收集语言的影响要小于C和C ++等手动管理语言。
答案 1 :(得分:1)
堆栈上的数据(比如C结构)在函数调用返回后消失。因此,需要复制和纠正指针。 想想这里需要隐藏的额外功能:
struct S* f() {
struct S s = ...;
g(&s);
return &s;
}
Java意味着简化,拥有自己的内存管理,并且在堆上立即执行操作似乎更直接,更简单。
这是对C ++的看法,包括它的拷贝构造函数,指针和别名。
答案 2 :(得分:0)
Java不允许显式对象的堆栈分配。该语言不与C等低级语言竞争,语言的创建者将此选择作为简化。
然而,随着时代的变迁,Java自其谦逊的开端以来一直在增长。随着JVM变得更加复杂,可以将对象自动分配到堆栈。其基本原理类似于C中的'register'关键字;让编译器管理低级细节。它做得比人类好。在Java中,自动将对象分配到堆栈中受到两个因素的阻碍,首先,Sun / Oracle JVM现在非常陈旧且非常复杂。很难改变,Oracle一直小心防止倒退。其次,到目前为止,他们在堆栈分配方面的工作尚未产生预期的巨大效益。它确实改善了一些情况,但JVM有自己的权衡和行为。所以这归结为时间/回报和优先事项的问题。我相信,改善自动分配的好处的工作仍在幕后进行;但是没有计划明确表达。
答案 3 :(得分:0)
简单来说,堆栈上对象的关键优势是可以自动管理内存。当函数将对象放在堆栈上时,它们会在函数出口处从内存中清除。
由于java已经有自动垃圾收集,这个关键优势并没有带来那么多。
当然,由于无法直接在堆栈上分配对象,您可能需要支付访问性能价格的速度,但正如Marko所提到的,内部优化可能就是这样。
答案 4 :(得分:-1)
除了原始类型之外,为什么一切都必须在堆上?
此声明不准确。如果原始类型是类实例的一部分,它们也可以在堆上。局部变量存储在堆栈中,其中类变量位于堆上。
至于为什么对象存储在堆上。这毕竟是一个设计决定。一个原因是它是JVM中受管理区域的垃圾收集。作为JVM中的托管区域,它可以按代组织,并且可以增大或缩小。请参阅JVM规范中的this section:
Java虚拟机具有在所有Java虚拟机线程之间共享的堆。堆是运行时数据区,从中分配所有类实例和数组的内存。
在虚拟机启动时创建堆。对象的堆存储由自动存储管理系统(称为垃圾收集器)回收;对象永远不会被明确释放。