我知道方法的变量存储在堆栈中,类变量存储在堆中。那么我们创建的类和对象在 Java 中存储在哪里呢?
8 回答
JVM中的运行时数据区可以划分如下,
方法区:已编译类文件的存储区。(每个 JVM 实例一个)
堆:对象的存储区域。(每个 JVM 实例一个)
Java栈:局部变量的存储区域,中间操作的结果。(每个线程一个)
PC 寄存器:如果下一条指令是本机方法,则存储要执行的下一条指令的地址,那么 pc 寄存器中的值将是未定义的。(每个线程一个)
本机方法堆栈:帮助执行本机方法(用 Java 以外的语言编写的方法)。(每个线程一个)
以下是关于 Java 中的内存分配需要考虑的几点。
笔记:
对象和对象引用是不同的东西。
Java中
new
经常使用关键字来创建新对象。但new
所做的是为您正在制作的类的对象分配内存并返回一个引用。这意味着,每当您将对象创建为静态或本地时,它都会存储在heap中。所有类变量原语或对象引用(它只是一个指向对象存储位置的指针,即堆)也存储在堆中。
ClassLoader
由静态变量和静态对象引用加载的类存储在永久代堆中的一个特殊位置。局部原始变量、局部对象引用和方法参数存储在堆栈中。
本地函数(方法)存储在堆栈中,但静态函数(方法)存储在永久存储中。
所有与类相关的信息,如类名、与类关联的对象数组、JVM 使用的内部对象(如 Java/Lang/Object)和优化信息都进入永久生成区域。
要了解堆栈、堆和数据,您应该阅读操作系统中的进程和进程控制块。
Java 中的所有对象都存储在堆上。持有对它们的引用的“变量”可以在堆栈上,也可以包含在其他对象中(那么它们不是真正的变量,而是字段),这也将它们放在堆上。
定义类的类对象也是堆对象。它们包含构成类的字节码(从类文件加载),以及由此计算的元数据。
内存的堆栈部分包含方法、局部变量和引用变量。
堆部分包含对象(也可能包含引用变量)。
在短暂的谷歌之后,我找到了一个描述它的链接,是的youtube 视频链接。^_^
这个概念很简单:
- 实例变量(原始、包装类、引用、对象(非静态))- 堆
- 局部变量,引用 - 堆栈
- 其他数据对象,如:类元数据、JVM 代码、静态变量、静态对象引用、静态函数等,以前在 Permgen 空间中(直到 Java 7)现在正在被移动到 JAVA 8 中的元空间。
PS:元空间是本机内存的一部分,所以现在不用担心 OOM:Pergem Exeption。
局部变量(方法变量)+方法存在于堆栈中。而对象及其实例变量位于堆上。
现在,引用变量可以是局部变量(如果在方法内部创建)或实例变量(如果在类内部创建,但在方法外部)。所以引用变量可以在任何地方,无论是堆栈还是堆。
根据 JVM 规范,
类和它自己的常量池,即静态变量存储在方法区。这里的类只是一堆字段,方法和常量,这些方法以指令的形式存储在方法区中,可以通过地址来识别。
对象只不过是一个填充类模板,将在 Heap Area 中创建,但对象引用是在Stack中创建的。
public class Sample{
int field;
static int constant;
public void test(){
int localVariable=10;
Sample samp=new Sample();
}
}
在示例中,sample.class 将进入方法区,这意味着在方法区中分配了“字段”、“常量”和方法“测试”。
开始执行时,由new Sample()创建的对象将进入堆,但“samp”只是一个对象引用,它进入堆栈并保存堆中存在的对象的地址
有关更多信息,请查看此链接, JVM 规范
在 Java 中,变量可以保存对象引用(s
inString s="OOO"
保存对对象的引用"OOO"
)或原始值(i
inint i=10
保存10
)。这些变量可以在堆上也可以在栈上,这取决于它们是否是本地的以及您使用的 JVM 的风格。
在 Java 虚拟机 8 规范文档 ( https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html ) 中,与此相关的内存组织如下。
每个 Java 线程都有自己的 JVM 堆栈。
它保存局部变量和部分结果,并在方法调用和返回中发挥作用。
堆栈中也可能有对象引用。只要它是本地的,规范就不会区分原始变量和引用变量。该堆栈使用称为帧的数据单元。然而,
因为除了推送和弹出帧外,Java 虚拟机堆栈永远不会被直接操作,因此帧可能是堆分配的。
因此,这取决于框架所在的 JVM 规范(如 OpenJDK 或 Oracle)的实现。
每个 JVM 实例都有一个堆。堆是
...运行时数据区域,所有类实例和数组的内存都从该区域分配。
保存对象数据的实际内存驻留在堆上。堆在所有 JVM 的线程之间共享。这还包括作为对象一部分的对象引用,即实例变量。
例如,参加以下课程。
class Moo{ private String string= "hello"; } class Spoo{ private Moo instanceReferenceVariable = new Moo(); public void poo(){ int localPrimitiveVariable=12; Set<String> localReferenceVariable = new HashSet<String>(); } }
在这里,变量引用的对象
instanceReferenceVariable
将在堆上,因此该对象的所有实例变量,如string
,也将在堆上。变量localPrimitiveVariable
和localReferenceVariable
将在堆栈上。方法区:方法区是堆的一个受限部分
... 存储每个类的结构,例如运行时常量池、字段和方法数据,以及方法和构造函数的代码,包括类和实例初始化以及接口初始化中使用的特殊方法。
运行时常量池:这是方法区的一部分。
希望这能够澄清事情。