为什么在Java中使用堆内存

6

为什么我们要使用堆内存,能否使用栈内存来替代?


编辑

在阅读了回答后,我又想到一个问题: 1)除了堆和栈,还有其他类型的内存可以作为替代吗?


编辑

我了解到了字符串池,那这个内存是与堆内存还是栈内存相关联的?

7个回答

12

如果想使用对象,就必须使用堆(heap)。对象本质上是指针,在栈(stack)或其他对象内指向堆中内存块。

与C++不同的是,在Java中需要以不同方式处理。即使在C++中,为了让对象超出创建函数生命周期而存在,使用堆也是一个好主意。你可以避免使用堆,但可能会因为所有复制构造函数而导致性能问题。


至于您的编辑:

有没有其他种类的内存可以用作堆和栈的替代品?

当然有:静态数据成员,每个类只有一个而不是每个实例化对象一个的数据成员必须放在某处。这些不能在栈上,因为它们可能在退出函数时消失。它们也不能属于任何特定对象。这些(至少在Sun/Oracle JVM中)放在方法区(method area)中。

此外,您不应认为只有一个栈。每个线程在创建时都有自己的栈。还有用于本地(非Java)调用的常量池和堆栈。

关于JVM内部有很多信息 在这里,但请记住,逻辑机器和实现之间可能会有区别。


6
请注意,Java的最新版本具有逃逸分析作为实验性功能,这使得JVM能够在堆栈上分配不从方法中逃逸的对象。请参见http://en.wikipedia.org/wiki/Escape_analysis - 但请注意,这只是一种内部优化,没有办法明确指定要在堆栈上分配对象。 - Jesper
由于Java处理对象,因此我们使用堆内存,或者一般来说,任何处理对象的语言都将使用堆内存。 - gmhk

2

堆内存是Java的核心。所有对象都在堆上分配,而局部变量仅保存对它们的引用(基本上是指针)。C#允许您拥有存储在堆栈上的对象(值类型)。Java选择不从C++中借鉴这一点,部分原因是为了简化语言。

跨语言,堆内存是提供任意大小的长寿命对象的方法。


由于Java处理对象,因此我们使用堆内存,或者一般来说,任何处理对象的语言都将使用堆内存。 - gmhk

2
我们能使用堆栈内存吗?
基本上不行。
在Java中,堆栈内存仅用于方法的参数和局部变量(以及一些隐藏的簿记信息,与此讨论无关)。对于这些变量,只有原始类型使用堆栈内存。任何对象或数组都表示为指向堆上某个东西的引用,并且Java没有提供在堆栈上分配对象或数组的方法。
[顺便说一下:理论上可以使用原始类型和递归静态方法在Java中执行任何计算。但是,这将非常混乱。即使编写简单的控制台消息也将导致应用程序在堆上分配对象。因此,我们可以将其视为完全不切实际。]

1

堆内存用于在Java中存储对象。无论对象在代码中是作为成员变量、局部变量还是类变量创建的,它们始终在Java的堆空间内创建。 如果堆栈中没有足够的内存来存储函数调用或局部变量,JVM将抛出java.lang.StackOverFlowError错误, 而如果没有更多的堆空间来创建对象,则JVM将抛出java.lang.OutOfMemoryError Java Heap Space错误。


1
在JVM中,它使用线程本地分配缓冲区(TLAB)代替对象的线程本地堆栈。这样可以获得堆栈的大部分性能优势,而无需开发人员担心对象是否在堆栈上。

0
另一个需要考虑的重要因素是作用域。如果所有对象都在堆栈上分配,那么随着堆栈帧被卷出,它们就会失去上下文。通俗地说,将堆栈视为存储所有变量值和对象引用的转储,这些变量值和对象引用是局部于当前范围内的子例程/方法的。一旦它执行完成(或方法超出范围),则其中的所有内容都将丢失。

你的意思是说栈只用于存储短生命周期的对象,不能长时间持有对象? - gmhk

0

这样编译器更容易管理大型和/或动态大小的变量-它们在调用堆栈上仍然只占用小的常量存储空间-即4个字节(一个堆指针)。


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