下面语句中的数据是自动分配内存还是动态分配内存,或者两者都存储
myFunction(new MyClass());
谢谢!
术语"自动内存分配"one_answers"动态内存分配"在Java上下文中没有任何意义。在Java中,所有内存都由执行环境管理。
在其他编程语言中,术语"自动存储"one_answers"动态存储"用于区分存储和存储,前者在超出作用域时自动释放,后者需要应用程序执行显式释放操作。在Java中,根本没有显式的释放。您将发现,人们和文献仍然在区分堆栈和堆,只有后者包含对象,其生命周期可能超过创建它们的方法的执行时间。然而,这只是一种逻辑分离,可能无法反映特定JVM实现的实际工作方式。
Java®语言规范并没有强制要求关于此工作的太多细节。只有两个位置:
15.12.4.5。创建帧,同步,传输控制
某个类
S
中的方法m
已被确定为要调用的方法。现在创建了一个新的激活帧,其中包含目标引用(如果有的话)和参数值(如果有的话),以及足够的空间用于局部变量和要调用的方法的堆栈,以及实现可能需要的任何其他簿记信息(堆栈指针、程序计数器、对先前激活帧的引用等)。如果没有足够的可用内存来创建这样的激活帧,则抛出
StackOverflowError
。
17.4.1。共享变量
可以在线程之间共享的内存称为共享内存或堆内存。所有实例字段、
static
字段和数组元素都存储在堆内存中。在本章中,我们使用术语变量来指代字段和数组元素。局部变量(§14.4)、形式方法参数(§8.4.1)和异常处理程序参数(§14.20)永远不会在线程之间共享,并且不受内存模型的影响。
请注意,这是唯一使用术语"堆"作为内存类型的地方,以及实际上是如何在这里定义的…
第15.9.4节。类实例创建表达式的运行时求值和§15.10.2。数组创建表达式的运行时计算更加模糊,说"空间已分配",如果没有足够的可用空间则抛出OutOfMemoryError
,仅此而已。
因此,如果您选择区分堆栈和堆的路线,您可以说您的代码myFunction(new MyClass());
将为MyClass
的实例进行堆分配,然后为myFunction
方法的实际实现的激活帧进行堆栈分配。这对任何实际目的都无关紧要。
如果您想更深入地了解jvm可以实现它的方法,您可以参考Java®虚拟机规范:
2.5.2。Java虚拟机栈
每个Java虚拟机线程都有一个私有的Java虚拟机堆栈,与线程同时创建。Java虚拟机堆栈存储帧(第2.6节)。Java虚拟机堆栈类似于传统语言(如C语言)的堆栈:它保存局部变量和部分结果,并在方法调用和返回中起作用。因为Java虚拟机堆栈从来不被直接操作,除了推送和弹出帧之外,帧可以被堆分配。Java虚拟机堆栈的内存不需要是连续的。
2.5.3。堆
Java虚拟机有一个堆,它被所有Java虚拟机线程共享。堆是为所有类实例和数组分配内存的运行时数据区。
堆在虚拟机启动时创建。对象的堆存储由自动存储管理系统(称为垃圾收集器)回收;对象永远不会显式释放。Java虚拟机没有特定类型的自动存储管理系统,存储管理技术可以根据实现者的系统需求来选择。堆的大小可以是固定的,也可以根据计算的需要进行扩展,如果不需要更大的堆,则可以收缩堆。堆内存不需要是连续的
请注意,这些定义只是在它们的目的上不同,而在它们的约束上没有显著的区别,即可能是固定大小的或可调整大小的,可能是连续的或不连续的,更不用说明确提到在堆上分配堆栈帧的可能性了。
Java中有三组内存
- Heap -这是当你调用new MyClass()时创建和定位对象的地方;
- Stack -堆栈包含堆栈帧,堆栈帧为原语和指向堆中的对象的指针分配了空间。栈帧在方法调用时分配,在方法返回时释放
- 字符串常量:在编译时定义的字符串字面量将被添加到字符串常量池中。在运行时,可以使用String.intern() 向池中添加字符串
就这样
在您的示例中分配了两个东西;
-
表达式,
new MyClass()
在对象堆上分配一个MyClass
的实例。 -
new
表达式的结果是对象引用。对象引用保存在调用myFunction()
的激活记录中。激活记录在调用线程的调用堆栈上分配。
当您创建一个新对象时,它通常在堆上分配,但是通过转义分析,它可以解压缩到堆栈中,就好像它是一个局部变量。
唯一的分配是通过new
或捕获lambda(或在极少数情况下一些本机方法)分配对象的不同方式之间没有任何显式的区别。