Java能在栈上分配列表吗?

16

每次我在Java中初始化一个列表时,我都会这样做:

List<Integer> list = new LinkedList<>();

我认为这将在堆上分配列表。不知道有没有办法在栈上分配列表?


1
你为什么想要在堆栈上分配它? - Puce
这是纯粹的学术研究吗? - wvdz
2
@LuiggiMendoza PermGen不是堆栈内存。它是分配类和其他结构的空间,以便在JVM进程的剩余时间内存在; 将这些移动到单独的空间使常规堆中的垃圾收集更有效。随着类卸载变得更加常见,这种效率提高变得不那么明显了。 - erickson
1
list在栈上,但这是一个引用,实际的对象(或对象们)在堆上。 - Peter Lawrey
2
@PeterLawrey 只有当它是局部变量时才会如此。如果它是成员变量,它也将在堆上。 - Puce
在理论上,逃逸分析可以执行对象规避,并且成员字段也将在堆栈上。但实际上,这几乎从不发生。 - Peter Lawrey
2个回答

16

所有对象,包括它们各自的属性,都存储在堆上。

所有本地变量及其参数都存储在栈上,因为它们包含原始值或引用。

然而,在特殊情况下,Java虚拟机可能执行逃逸分析并决定在堆栈上分配对象(包括您的LinkedList),但这通常不会发生,也不是一个主要问题。

作为一般规则,如果您在堆栈上分配对象,则在调用引用该对象的函数时,将获得该对象的副本。相反,如果您在堆上分配对象,则在传递指向对象的指针时,将获得指针的副本(该指针指向完全相同的堆上的对象)。


那么C++呢?似乎C++可以通过执行“list<int> a;”在堆栈上分配一个列表。然后它可以执行任何所需的操作。 - user2640480
2
@user2640480 这只将第一个/根对象放在堆栈上,其余节点必须在堆上。 - Peter Lawrey
一般来说,如果你在栈上分配一个对象,在调用引用该对象的函数时会得到该对象的副本;但如果你在堆上分配它,当你传递指向该对象的指针时,你将得到指针的副本(指向堆上完全相同的对象)。@user264080 - manan
这样,在C++中,我猜我不能将对象(在堆栈上分配)作为函数返回,因为它将随着堆栈弹出。是这样吗? - user2640480
当在“堆栈”上进行分配时,这是指JVM堆栈还是用于C/C++本地变量的C/C++堆栈? - nanofarad

13

理论上,JVM实现可以使用“逃逸分析”在堆栈上分配对象。如果可以确定对创建的对象的引用永远不会泄漏到堆之外,则JVM可以将其分配到堆栈上而不是堆上。这样做的好处是减少垃圾回收开销;当退出堆栈帧时,该内存可以立即被回收。它还可能提高速度,因为引用位置的局部性。

从Java 7开始,在Oracle的HotSpot Java运行时中引入了逃逸分析。通过此增强版本,HotSpot可能选择不分配未修改的堆栈本地对象;而不是将它们分配到堆栈上,它直接删除了分配。虽然这没有完全实现堆栈分配,但它表明这样的事情是可行的运行时优化。

然而,Java程序员没有办法直接控制这种行为。这是JIT编译器执行的优化。我不确定语言规范是否允许在编译时进行这种优化。它可能允许,但我没有研究过。


据我所知,EscapeAnalysis 出现在 Java 6 版本 17 中。有详细的状况调查 - Grigory Kislin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接