首先要做的事情:我假设您阅读了this文章(因为那里我看到一个非常类似于您的图表),所以我不会引用或突出显示那篇文章中提到的任何观点,并尝试回答您在那篇文章中没有涉及到的问题。
阅读您所有的问题,我的印象是您清楚地知道内存是如何在堆栈中分配的,但对于类的元数据有疑问,即类的方法存储在内存的哪个位置以及如何回收它们。因此,让我首先尝试解释JVM内存区域:
JVM内存区域
让我从这两张描述JVM内存区域的图开始:
图表来源
![enter image description here](https://istack.dev59.com/HlGXu.webp)
图表来源
![enter image description here](https://istack.dev59.com/eP0SJ.webp)
现在,如上图所示,JVM内存的树形结构清晰可见。我将尝试阐明同样的内容(@Adit:请注意您关心的区域是PermGen Space或非堆内存的永久代)。
堆内存
- 年轻代
- Eden空间
- Survivor空间
- 老年代
- Tenured Generation
非堆内存
- 永久代
- 代码缓存(我认为只有HotSpot Java VM包含)
堆内存是Java虚拟机为所有类实例和数组分配内存的运行时数据区。堆可以是固定大小或可变大小。垃圾收集器是一种自动内存管理系统,用于回收对象的堆内存。
年轻代是创建所有新对象的地方。当年轻代填满时,会执行垃圾收集。这种垃圾收集称为Minor GC。年轻代分为以下两部分。
伊甸园空间: 大多数对象最初被分配内存的池。
幸存者空间: 包含在伊甸园空间垃圾收集后幸存的对象的池。
老年代
老年代包含长期存在并在多次 Minor GC 后仍然存活的对象。通常,当老年代满时进行垃圾收集。老年代垃圾收集称为 Major GC,并且通常需要更长时间。老年代包括以下部分:
持久代: 包含在幸存者空间存活一段时间的对象的池。
非堆内存
非堆内存包括一个供所有线程共享的方法区和 Java VM 内部处理或优化所需的内存。它存储每个类的结构,例如运行时常量池、字段和方法数据以及方法和构造函数的代码。方法区在逻辑上属于堆的一部分,但是根据实现,Java VM 可能不会对其进行垃圾回收或压缩。与堆内存一样,方法区可能具有固定或可变的大小。方法区的内存不需要连续。
永久代
池包含虚拟机本身的所有反射数据,例如类和方法对象。对于使用类数据共享的Java VM,此代被分为只读区域和读写区域。
代码缓存
HotSpot Java VM还包括一个代码缓存,其中包含用于编译和存储本机代码的内存。
具体回答OP的问题
s的方法存储在哪里?
非堆内存 --> 永久代
如果我在myMethod中创建了另一个MemoryClass对象,JVM是否会在堆栈内存中为相同的方法再次分配内存?
堆栈内存仅包含局部变量,因此您的new MemoryClass
的ORV(对象引用变量)仍将在myMethod
的堆栈帧中创建,但是JVM不会再次加载所有MemoryClass
的方法、元数据等到"永久代"。
JVM只加载一次类,当它加载类时,为该类在“永久代”上分配空间,而这仅在JVM加载类时发生一次。
如果我Method的执行完成后JVM释放分配给其的内存,如果JVM多次分配内存给相同的方法,它会如何管理所述情况?
Stack frame created for
myMethod
将从栈内存中删除,因此为局部变量创建的所有内存都将清除,但这并不意味着JVM将清理为
myMethod
创建对象分配在"永久代"中的内存。
如果我只声明s而没有初始化它,情况会怎样?JVM是否仍会为java.lang.String类的所有方法分配内存?如果是,为什么?
具体而言,对于
String
类,JVM会在太早的时候以“永久代”的方式分配
String
的空间,而无论您是否初始化字符串变量,从“永久代”的角度来看都没关系。
就其他用户定义的类而言,JVM将在定义类时加载类并在“永久代”中分配内存,即使您不创建该类的对象,也会在“永久代”(
非堆区域)中分配内存,当您创建一个类的对象时,内存将在“伊甸园空间”(
堆区域)中分配。
以上信息来源和进一步阅读: