Java 栈和堆内存管理
Java stack and heap memory management
我想知道在下面的程序中内存是如何分配的:
public class MemoryClass {
public static void main(final String[] args) {
int i = 0;
MemoryClass memoryClass = new MemoryClass();
memoryClass.myMethod(memoryClass);
}
private void myMethod(final Object obj) {
int i = 1;
String s = "HelloWorld!";
}
}
现在,据我的理解,下图描述了内存分配是如何发生的:
上图中,栈内存中的memory、obj和s,实际上是对放置在堆内存中的“实际对象”的引用。
以下是我想到的一组问题:
- s的方法存储在哪里?
- 如果我在
myMethod
中创建了另一个 MemoryClass
的对象,JVM 会在堆栈内存中再次为相同的方法分配内存吗?
- JVM 是否会在执行完成后立即释放分配给
myMethod
的内存,如果是,它将如何处理问题 2 中提到的情况(仅在 JVM 分配内存时适用多次使用相同的方法).
- 如果我只声明了s而没有初始化它,那么JVM还会为
java.lang.String
的所有方法分配内存吗class,如果是,为什么?
Where are the methods of s stored?
它们存储在Stringclass对象中;它是在程序中首次引用String时由ClassLoader对象加载的对象。当我最后读到这篇文章时存在的所有 JVM 实现都不会在 class 对象加载后释放内存。它在堆上。
Had I created another object of MemoryClass inside myMethod, would JVM allocate memory for the same methods again inside the stack memory?
不,对象的方法和数据是分开保存的,特别是因为 JVM 永远不需要多个方法副本。
Would JVM free the memory allocated to myMethod as soon as it's execution is completed, if so, how would it manage the situation mentioned in question 2(only applicable if JVM allocates memory multiple times to the same method).
没有。 Java 通常不会 "immediately free memory" 的东西存储在堆上。它会使事情 运行 变得太慢。它仅在垃圾收集器 运行 时释放内存,并且仅当垃圾收集器的 运行 算法决定时间到了时才会释放内存。
What would have been the case, if I had only declared s and did not initialize it, would JVM still allocate memory to all the methods of java.lang.String class,if so,why?
我认为这取决于 JVM 实现,也许还有编译器。如果您声明了一个变量并且从不使用它,编译器很可能(并且很常见)注意到它没有用处并且不会将它放入 class 文件中。如果它不在 class 文件中,它永远不会被引用,因此它和它的方法不会被加载,等等。如果编译器将它放入但从未被引用,那么 ClassLoader 就不会有任何理由加载它,但我对它是否会加载有点含糊。可能取决于 JVM 实现;它加载东西是因为有 class 的变量还是仅当它们被引用时?有多少种ClassLoader算法可以在4位PIN码的头部跳舞?
我鼓励您阅读 JVM 和 ClassLoader 等相关内容;通过阅读对其工作原理的解释,而不是用你能想到的例子来研究它,你会收获更多。
要事第一:我假设你的问题是在阅读 this 文章后提出来的(因为在那里我看到了一个与你的图表非常相似的图表) 所以我不会引用或突出显示那里提到的任何要点,并会尝试用不是很明显的要点来回答你的问题post.
看了你所有的问题,我的印象是你很清楚内存是如何在堆栈和堆中分配的,但对 classes 的元数据有疑问,即在内存中的位置,classes 将被存储以及它们将如何被回收。那么,首先让我尝试解释一下 JVM 内存区域:
JVM 内存区域
让我先放这两个描述 JVM 内存区域的图表:
Source of diagram
现在,从上图可以清楚地看出 JVM 内存的树结构,我将尝试阐明它(@Adit:请注意,您关注的区域是 PermGen Space或永久代space非堆内存).
- 堆内存
- 年轻一代
- 伊甸园Space
- 幸存者Space
- 老一代
- 长辈
- 非堆内存
- 永久代
- 代码缓存(我认为包含在 HotSpot Java VM 中 "only")
堆内存
堆内存是运行时数据区域,Java VM 从中为所有 class 实例和数组分配内存。堆的大小可以是固定的或可变的。垃圾收集器是一个自动内存管理系统,可以为对象回收堆内存。
年轻一代
年轻代是创建所有新对象的地方。当年轻代被填满时,就会进行垃圾回收。这种垃圾回收称为 Minor GC。新生代分为以下两部分
Eden space:最初为大多数对象分配内存的池。
Survivor space: 包含在 Eden 的垃圾收集中幸存下来的对象的池 space.
老一代
Old Generation memory包含了经过多轮Minor GC后存活时间长的对象。通常垃圾回收是在老年代内存满的时候进行的。 Old Generation Garbage Collection称为Major GC,通常需要更长的时间。老一代包含以下部分:
Tenured space: 包含在幸存者中存在一段时间的对象的池 space.
非堆内存
非堆内存包括所有线程共享的方法区和 Java VM 的内部处理或优化所需的内存。它存储每个 class 结构,例如运行时常量池、字段和方法数据,以及方法和构造函数的代码。方法区在逻辑上是堆的一部分,但是根据实现的不同,Java VM 可能不会对其进行垃圾收集或压缩。和堆内存一样,方法区的大小可以是固定的,也可以是可变的。方法区的内存不需要连续。
永久代
包含虚拟机自身所有反射数据的池,例如class和方法对象。有Java台使用class数据共享的虚拟机,这一代分为只读区和读写区。
代码缓存
HotSpot Java VM 还包含一个代码缓存,包含用于编译和存储本机代码的内存。
专门回答OP的问题
Where are the methods of s stored?
非堆内存 --> 永久代
Had I created another object of MemoryClass inside myMethod, would JVM
allocate memory for the same methods again inside the stack memory?
堆栈内存仅包含局部变量,因此您的新 MemoryClass
的 ORV(对象引用变量)仍会在 myMethod
的堆栈帧中创建,但 JVM 不会加载所有方法、元数据MemoryClass
等 "Permanent Generation".
JVM 只加载 class 一次,当它加载 class 时,space 被分配到 "Permanent Generation" 上 class 并且只发生一次而 class 由 JVM 加载。
Would JVM free the memory allocated to myMethod as soon as it's
execution is completed, if so, how would it manage the situation
mentioned in question 2(only applicable if JVM allocates memory
multiple times to the same method).
为myMethod
创建的栈帧将从栈内存中删除,因此所有为局部变量创建的内存都将被清理,但这并不意味着JVM会清理在[=160中分配的内存=] 对于 class 您在 myMethod
中创建的那些对象
What would have been the case, if I had only declared s and did not
initialize it, would JVM still allocate memory to all the methods of
java.lang.String class, if so, why?
具体来说 String
class,JVM 会在 "Permanent Generation" 中为 String
分配 space 太早了,而 JVM 启动时是否您是否初始化 String 变量,从 "Permanent Generation" 的角度来看并不重要。
谈到其他用户定义的 classes,JVM 会加载 class 并在您定义 class 后立即在 "Permanent Generation" 中分配内存,甚至如果你不创建class的对象,内存分配在"Permanent Generation"(非堆区)并且当你创建[=的对象时174=]则内存分配在"Eden Space"(堆区)。
以上信息来源及延伸阅读:
- http://docs.oracle.com/javase/7/docs/technotes/guides/management/jconsole.html
- http://www.journaldev.com/2856/java-jvm-memory-model-memory-management-in-java
- https://blogs.oracle.com/jonthecollector/entry/presenting_the_permanent_generation
- Java heap terminology: young, old and permanent generations?
既然arsy接受的答案和hagrawal的答案很清楚,就想详细说明第四个问题:
What would have been the case, if I had only declared s and did not
initialize it, would JVM still allocate memory to all the methods of
java.lang.String class,if so,why?
基本上,class 数据(具有字段和方法信息)确实存储在永久代中(从 JDK-8 开始的元space ), 重要的是要注意它在 java.lang.String class 中的对象(例如 char[] 保存该字符串的所有字符信息),其数据在堆上分配。
在创建新的字符串对象之前不会发生这种情况 - 使用 'new' 关键字或通过创建新的字符串文字(例如:"helloworld")。
我想知道在下面的程序中内存是如何分配的:
public class MemoryClass {
public static void main(final String[] args) {
int i = 0;
MemoryClass memoryClass = new MemoryClass();
memoryClass.myMethod(memoryClass);
}
private void myMethod(final Object obj) {
int i = 1;
String s = "HelloWorld!";
}
}
现在,据我的理解,下图描述了内存分配是如何发生的:
上图中,栈内存中的memory、obj和s,实际上是对放置在堆内存中的“实际对象”的引用。
以下是我想到的一组问题:
- s的方法存储在哪里?
- 如果我在
myMethod
中创建了另一个MemoryClass
的对象,JVM 会在堆栈内存中再次为相同的方法分配内存吗? - JVM 是否会在执行完成后立即释放分配给
myMethod
的内存,如果是,它将如何处理问题 2 中提到的情况(仅在 JVM 分配内存时适用多次使用相同的方法). - 如果我只声明了s而没有初始化它,那么JVM还会为
java.lang.String
的所有方法分配内存吗class,如果是,为什么?
Where are the methods of s stored?
它们存储在Stringclass对象中;它是在程序中首次引用String时由ClassLoader对象加载的对象。当我最后读到这篇文章时存在的所有 JVM 实现都不会在 class 对象加载后释放内存。它在堆上。
Had I created another object of MemoryClass inside myMethod, would JVM allocate memory for the same methods again inside the stack memory?
不,对象的方法和数据是分开保存的,特别是因为 JVM 永远不需要多个方法副本。
Would JVM free the memory allocated to myMethod as soon as it's execution is completed, if so, how would it manage the situation mentioned in question 2(only applicable if JVM allocates memory multiple times to the same method).
没有。 Java 通常不会 "immediately free memory" 的东西存储在堆上。它会使事情 运行 变得太慢。它仅在垃圾收集器 运行 时释放内存,并且仅当垃圾收集器的 运行 算法决定时间到了时才会释放内存。
What would have been the case, if I had only declared s and did not initialize it, would JVM still allocate memory to all the methods of java.lang.String class,if so,why?
我认为这取决于 JVM 实现,也许还有编译器。如果您声明了一个变量并且从不使用它,编译器很可能(并且很常见)注意到它没有用处并且不会将它放入 class 文件中。如果它不在 class 文件中,它永远不会被引用,因此它和它的方法不会被加载,等等。如果编译器将它放入但从未被引用,那么 ClassLoader 就不会有任何理由加载它,但我对它是否会加载有点含糊。可能取决于 JVM 实现;它加载东西是因为有 class 的变量还是仅当它们被引用时?有多少种ClassLoader算法可以在4位PIN码的头部跳舞?
我鼓励您阅读 JVM 和 ClassLoader 等相关内容;通过阅读对其工作原理的解释,而不是用你能想到的例子来研究它,你会收获更多。
要事第一:我假设你的问题是在阅读 this 文章后提出来的(因为在那里我看到了一个与你的图表非常相似的图表) 所以我不会引用或突出显示那里提到的任何要点,并会尝试用不是很明显的要点来回答你的问题post.
看了你所有的问题,我的印象是你很清楚内存是如何在堆栈和堆中分配的,但对 classes 的元数据有疑问,即在内存中的位置,classes 将被存储以及它们将如何被回收。那么,首先让我尝试解释一下 JVM 内存区域:
JVM 内存区域
让我先放这两个描述 JVM 内存区域的图表:
Source of diagram
现在,从上图可以清楚地看出 JVM 内存的树结构,我将尝试阐明它(@Adit:请注意,您关注的区域是 PermGen Space或永久代space非堆内存).
- 堆内存
- 年轻一代
- 伊甸园Space
- 幸存者Space
- 老一代
- 长辈
- 年轻一代
- 非堆内存
- 永久代
- 代码缓存(我认为包含在 HotSpot Java VM 中 "only")
堆内存
堆内存是运行时数据区域,Java VM 从中为所有 class 实例和数组分配内存。堆的大小可以是固定的或可变的。垃圾收集器是一个自动内存管理系统,可以为对象回收堆内存。
年轻一代
年轻代是创建所有新对象的地方。当年轻代被填满时,就会进行垃圾回收。这种垃圾回收称为 Minor GC。新生代分为以下两部分
Eden space:最初为大多数对象分配内存的池。
Survivor space: 包含在 Eden 的垃圾收集中幸存下来的对象的池 space.
老一代
Old Generation memory包含了经过多轮Minor GC后存活时间长的对象。通常垃圾回收是在老年代内存满的时候进行的。 Old Generation Garbage Collection称为Major GC,通常需要更长的时间。老一代包含以下部分:
Tenured space: 包含在幸存者中存在一段时间的对象的池 space.
非堆内存
非堆内存包括所有线程共享的方法区和 Java VM 的内部处理或优化所需的内存。它存储每个 class 结构,例如运行时常量池、字段和方法数据,以及方法和构造函数的代码。方法区在逻辑上是堆的一部分,但是根据实现的不同,Java VM 可能不会对其进行垃圾收集或压缩。和堆内存一样,方法区的大小可以是固定的,也可以是可变的。方法区的内存不需要连续。
永久代
包含虚拟机自身所有反射数据的池,例如class和方法对象。有Java台使用class数据共享的虚拟机,这一代分为只读区和读写区。
代码缓存
HotSpot Java VM 还包含一个代码缓存,包含用于编译和存储本机代码的内存。
专门回答OP的问题
Where are the methods of s stored?
非堆内存 --> 永久代
Had I created another object of MemoryClass inside myMethod, would JVM allocate memory for the same methods again inside the stack memory?
堆栈内存仅包含局部变量,因此您的新 MemoryClass
的 ORV(对象引用变量)仍会在 myMethod
的堆栈帧中创建,但 JVM 不会加载所有方法、元数据MemoryClass
等 "Permanent Generation".
JVM 只加载 class 一次,当它加载 class 时,space 被分配到 "Permanent Generation" 上 class 并且只发生一次而 class 由 JVM 加载。
Would JVM free the memory allocated to myMethod as soon as it's execution is completed, if so, how would it manage the situation mentioned in question 2(only applicable if JVM allocates memory multiple times to the same method).
为myMethod
创建的栈帧将从栈内存中删除,因此所有为局部变量创建的内存都将被清理,但这并不意味着JVM会清理在[=160中分配的内存=] 对于 class 您在 myMethod
What would have been the case, if I had only declared s and did not initialize it, would JVM still allocate memory to all the methods of java.lang.String class, if so, why?
具体来说 String
class,JVM 会在 "Permanent Generation" 中为 String
分配 space 太早了,而 JVM 启动时是否您是否初始化 String 变量,从 "Permanent Generation" 的角度来看并不重要。
谈到其他用户定义的 classes,JVM 会加载 class 并在您定义 class 后立即在 "Permanent Generation" 中分配内存,甚至如果你不创建class的对象,内存分配在"Permanent Generation"(非堆区)并且当你创建[=的对象时174=]则内存分配在"Eden Space"(堆区)。
以上信息来源及延伸阅读:
- http://docs.oracle.com/javase/7/docs/technotes/guides/management/jconsole.html
- http://www.journaldev.com/2856/java-jvm-memory-model-memory-management-in-java
- https://blogs.oracle.com/jonthecollector/entry/presenting_the_permanent_generation
- Java heap terminology: young, old and permanent generations?
既然arsy接受的答案和hagrawal的答案很清楚,就想详细说明第四个问题:
What would have been the case, if I had only declared s and did not initialize it, would JVM still allocate memory to all the methods of java.lang.String class,if so,why?
基本上,class 数据(具有字段和方法信息)确实存储在永久代中(从 JDK-8 开始的元space ), 重要的是要注意它在 java.lang.String class 中的对象(例如 char[] 保存该字符串的所有字符信息),其数据在堆上分配。
在创建新的字符串对象之前不会发生这种情况 - 使用 'new' 关键字或通过创建新的字符串文字(例如:"helloworld")。