Java应用程序启动时,所有线程都有一个堆。每个线程都有自己的堆栈。
启动Java应用程序时,我们使用JVM选项-Xms
和-Xmx
来控制堆的大小,使用-Xss
来控制堆栈大小。
我的理解是,正在创建的堆变为"托管" JVM的内存和所有正在创建的对象都放在那里。
但堆栈创建如何工作? Java是否在创建每个线程时为其创建堆栈?如果是这样,堆栈在内存上的确切位置?它肯定不在"托管"堆。
JVM是否从本机内存创建堆栈,还是为堆栈预先分配了一段托管内存区域?如果是这样,JVM如何知道如何创建线程?
答案 0 :(得分:57)
Java specification告诉我们有关于线程堆栈的一些事情。除其他外:
每个Java虚拟机线程都有一个私有Java虚拟机堆栈,与线程同时创建。
因为除了推送和弹出帧之外,永远不会直接操作Java虚拟机堆栈,因此可以对堆进行堆分配。 Java虚拟机堆栈的内存不需要是连续的。
规范允许Java虚拟机堆栈具有固定大小或根据计算要求动态扩展和收缩。
现在,如果我们专注于像HotSpot这样的JVM实现,我们可以获得更多信息。以下是我从不同来源收集的一些事实:
-Xss
选项的用途。
(Source) 在Java SE 6中,Sparc上的默认值在32位VM中为512k,在64位VM中为1024k。 ...您可以通过使用-Xss选项运行来减少堆栈大小。 ... 64k是每个线程允许的最小堆栈空间。
请注意,JVM使用的内存多于堆。例如,Java方法,线程堆栈和本机句柄分配在与堆不同的内存中,以及JVM内部数据结构。
它使用单独的软件堆栈来传递Java参数,而本机C堆栈则由VM本身使用。许多JVM内部变量(例如程序计数器或Java线程的堆栈指针)都存储在C变量中,这些变量不能保证始终保存在硬件寄存器中。这些软件解释器结构的管理占用了总执行时间的相当大的份额。
JVM可以使用一种称为转义分析的技术,通过该技术,它们可以判断某些对象在其整个生命周期内仍然局限于单个线程,并且该生命周期受给定堆栈帧的生命周期的限制。这些对象可以安全地分配到堆栈而不是堆。
因为图片胜过千言万语,所以来自James Bloom
现在回答你的一些问题:
JVM如何知道如何创建线程?
没有。通过创建可变数量的线程可以很容易地通过矛盾来证明。它确实对最大线程数和每个线程的堆栈大小做了一些假设。这就是为什么如果分配太多线程,你可能会耗尽内存(不是指堆内存!)。
Java在创建每个线程时是否为其创建堆栈?
如前所述,每个Java虚拟机线程都有一个私有Java虚拟机堆栈,与线程同时创建。 (Source)。
如果是这样,堆栈在内存上的确切位置?它肯定不在“托管”堆中。
如上所述,从技术上讲,Java specification允许堆栈内存存储在堆上。但至少JRockit JVM使用不同的内存部分。
JVM是否从本机内存创建堆栈,还是为堆栈预先分配了一段托管内存区域?
堆栈是JVM管理的,因为Java规范prescribes它必须如何表现: Java虚拟机堆栈存储帧(第2.6节)。 Java虚拟机堆栈类似于传统语言的堆栈。一个例外是用于native
方法的Native Method堆栈。有关此内容的更多信息请参见the specification。
答案 1 :(得分:5)
JVM使用的内存多于堆。例如Java方法, 线程堆栈和本机句柄分配在内存中 堆,以及JVM内部数据结构。
所以回答你的问题:
Java在创建每个线程时是否为其创建堆栈?
是
如果是这样,那么堆栈究竟在哪里?
在JVM分配的内存中,但不在堆上。
如果是这样,JVM如何知道如何创建线程?
它没有。
您可以创建尽可能多的内容,直到您最大化JVM内存并获得
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
编辑:
以上所有内容都涉及Jrockit JVM,尽管我发现很难相信其他JVM在这些基本问题上会有所不同。