首先:我假设你的问题在阅读本文后出来(因为在那边我看到了一个和你非常相似的图表),所以我不会引用或强调那里提到的任何观点,并会尝试用在那篇文章中不那么明显的点来回答你的问题。
阅读您的所有问题,我的印象是,您很清楚如何在堆栈和堆中分配内存,但对类的元数据有疑问,即在内存中的位置,类的方法将被存储以及如何回收。所以,首先让我试着解释一下JVM内存区域:
JVM 内存区域
让我首先放置这两个描述JVM内存区域的图表:
图表来源
图表来源
现在,从下面的上图中可以清楚地看出JVM内存的树结构,我将尝试阐明相同的结构(@Adit:请注意,您关注的区域是PermGen空间或非堆内存的永久生成空间)。
-
堆内存
-
非剧组记忆
- 永久生成
- 代码缓存(我认为“仅”包含 HotSpot Java VM)
堆内存
堆内存是运行时数据区域,Java VM 从中为所有类实例和数组分配内存。堆的大小可以是固定的,也可以是可变的。垃圾回收器是一种自动内存管理系统,用于回收对象的堆内存。
年轻一代
年轻一代是创造所有新对象的地方。当年轻一代被填满时,进行垃圾收集。此垃圾回收称为次要 GC。年轻一代分为以下2部分
伊甸园空间:最初从中为大多数对象分配内存的池。
幸存者空间:包含已在伊甸园空间的垃圾回收中幸存下来的对象的池。
老一辈
旧一代记忆包含长时间存在并在多次小GC之后幸存下来的物体。通常,垃圾回收在旧一代内存中执行,当它已满时。旧一代垃圾回收称为主要GC,通常需要更长的时间。老一代包含以下部分:
终身空间:包含幸存者空间中已存在一段时间的物体的池。
非堆内存
非堆内存包括在 Java VM 的内部处理或优化所需的所有线程和内存之间共享的方法区域。它存储每个类的结构,如运行时常量池、字段和方法数据,以及方法和构造函数的代码。方法区域在逻辑上是堆的一部分,但根据实现的不同,Java VM 可能不会对其进行垃圾回收或压缩。与堆内存一样,方法区域可以是固定大小或可变大小。方法区域的内存不需要是连续的。
永久生成
包含虚拟机本身的所有反射数据(如类和方法对象)的池。对于使用类数据共享的 Java VM,这一代分为只读和读写区域。
代码缓存
HotSpot Java VM 还包括一个代码缓存,其中包含用于编译和存储本机代码的内存。
具体回答OP的问题
s 的方法存储在哪里?
非堆内存 --> 永久生成
如果我在 myMethod 中创建了另一个 MemoryClass 对象,JVM 是否会在堆栈内存中再次为相同的方法分配内存?
堆栈内存仅包含局部变量,因此您的 ORV(对象引用变量)的 new 仍将在 的堆栈帧中创建,但 JVM 不会在“永久生成”中再次加载所有方法、元数据等。MemoryClass
myMethod
MemoryClass
JVM 只装入类一次,当它装入类时,会在“永久生成”上为该类分配空间,并且当类由 JVM 装入时,这种情况只发生一次。
JVM是否会在执行完成后立即释放分配给myMethod的内存,如果是这样,它将如何管理问题2中提到的情况(仅适用于JVM多次分配给同一方法的内存)。
创建的堆栈帧将从堆栈内存中删除,因此将为局部变量创建的所有内存都将被清除,但这并不意味着 JVM 将清理在“永久生成”中为您在其中创建的这些对象的类分配的内存myMethod
myMethod
如果我只是声明了s而没有初始化它,JVM是否仍然会将内存分配给java.lang.String类的所有方法,如果是这样,为什么?
具体来说,JVM会过早地在“永久生成”中分配空间,而JVM启动并且是否初始化String变量,从“永久生成”的角度来看并不重要。String
String
谈到其他用户定义的类,JVM 会在您定义类后立即加载该类并在“永久生成”中分配内存,同样,即使您不创建该类的对象,内存也会在“永久生成”(非堆区域)中分配,当您创建该类的对象时,内存将分配在“Eden Space”(堆区域)中。
以上信息来源及延伸阅读: