Java堆栈和堆内存管理
我想知道如何在以下程序中分配内存:
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!";
}
}
现在,据我的理解,下图描述了内存分配是如何发生的:
在上面的图中,堆栈内存中的内存 , obj和s实际上是对其放置在堆内存中的“ 实际对象 ”的引用。
这是我脑海中出现的一系列问题:
myMethod
创建了MemoryClass
另一个对象,那么JVM会在堆栈内存中为相同的方法再次分配内存吗? myMethod
的内存,如果是这样,它将如何管理问题2中提到的情况(仅适用于JVM为同一方法多次分配内存)。 java.lang.String
类的所有方法,如果是这样,为什么? s存储的方法在哪里?
它们存储在String类对象中; 它是在程序中首次引用String时由ClassLoader对象加载的对象。 JVM的所有实现在我读完这篇文章时都存在,一旦它被加载,它就不会为类对象释放内存。 它在堆上。
如果我在myMethod中创建了MemoryClass的另一个对象,那么JVM会在堆栈内存中为相同的方法再次分配内存吗?
不,对象的方法和数据是分开保存的,具体原因是JVM永远不需要超过一个方法副本。
如果JVM在执行完成后立即释放分配给myMethod的内存,如果是这样,它将如何管理问题2中提到的情况(仅适用于JVM为同一方法多次分配内存)。
不,Java通常不会立即释放存储在堆上的内容。 这会让事情运行得太慢。 它仅在垃圾收集器运行时释放内存,并且只有在其运行垃圾回收器的算法决定它是时间时才会这样做。
如果我只声明了s并且没有初始化它,那么情况会如何呢?JVM是否仍然将内存分配给java.lang.String类的所有方法,如果是这样,为什么?
这取决于JVM的实现,我认为,也许是编译器。 如果你声明了一个变量并且从不使用它,那么编译器很有可能(也是常见)注意到它没有用处,也没有把它放到类文件中。 如果它不在类文件中,它就不会被引用,因此它和它的方法不会被加载,等等。如果编译器无论如何都放它,但它从未被引用,那么ClassLoader将没有任何理由加载它,但我有点模糊,它是否会加载或不加载。 可能依赖于JVM的实现; 它会加载的东西,因为有类的变量或只有当它们被引用? 有多少ClassLoader算法可以在4位PIN码的头部跳舞?
我鼓励您阅读有关JVM和ClassLoaders等内容; 你会通过阅读关于它如何工作的解释而不是用你能想到的例子来嘲笑它来获得更多的收益。
首先要做的事情是 :假设你的问题是在阅读本文后出现的(因为在那里我看到一个非常类似于你的图),所以我不会引用或突出显示在那里提到的任何点,并会尝试回答用你在那篇文章中并不那么明显的点来回答你的问题。
读到你所有的问题,我的印象是,你清楚如何在堆栈和堆中分配内存,但是对类的元数据有怀疑,例如在内存中什么地方存储类的方法以及如何回收它们。 因此,首先让我尝试解释JVM内存区域:
JVM内存区域
让我开始将这两个图表描绘为JVM内存区域:
图的来源
图的来源
现在,从上面的图中可以清楚地看到JVM内存的树形结构,我将尝试对其进行说明( @Adit:请注意与您有关的区域是PermGen空间或非堆内存的永久生成空间 ) 。
堆内存
堆内存是Java VM为其分配所有类实例和阵列的内存的运行时数据区域。 堆可以是固定的或可变的大小。 垃圾收集器是一个自动内存管理系统,可以为对象回收堆内存。
年轻一代
年轻一代是所有新对象创建的地方。 年轻一代填补时,垃圾收集进行。 这个垃圾收集称为次要GC。 年轻一代分为以下2部分
Eden空间:最初为大多数对象分配内存的池。
幸存者空间(Survivor space):含有在伊甸园空间的垃圾收集中存活的对象的池。
老一辈
老一代记忆包含很长一段时间并在多轮次GC之后存活的物体。 垃圾收集通常在旧代内存中完成时进行。 老一代垃圾收集被称为主要GC,通常需要更长的时间。 旧一代包含以下部分:
终身空间:包含存活空间中已存在一段时间的物体的池。
非堆内存
非堆内存包括在Java VM的内部处理或优化所需的所有线程和内存之间共享的方法区域。 它存储每类结构,如运行时常量池,字段和方法数据以及方法和构造函数的代码。 方法区域在逻辑上是堆的一部分,但根据实现的不同,Java VM可能不会垃圾收集或压缩它。 像堆内存一样,方法区域可以是固定或可变的大小。 方法区域的内存不需要是连续的。
永久的一代
该池包含虚拟机本身的所有反射数据,例如类和方法对象。 对于使用类数据共享的Java虚拟机,这一代分为只读和读写区域。
代码缓存
HotSpot Java VM还包含一个代码缓存,其中包含用于编译和存储本机代码的内存。
专门回答OP的问题
s存储的方法在哪里?
非堆内存 - >永久生成
如果我在myMethod中创建了MemoryClass的另一个对象,那么JVM会在堆栈内存中为相同的方法再次分配内存吗?
堆栈内存只包含局部变量,所以新的MemoryClass
ORV(对象引用变量)仍将在myMethod
堆栈框架中创建,但JVM不会在“永久生成”中再次加载MemoryClass
所有方法,元数据等。
JVM只加载一次类,并且当它加载类时,空间将在该类的“永久生成”上分配,并且该类在JVM加载时只发生一次。
如果JVM在执行完成后立即释放分配给myMethod的内存,如果是这样,它将如何管理问题2中提到的情况(仅适用于JVM为同一方法多次分配内存)。
为myMethod
创建的堆栈框架将从堆栈内存中删除,因此为本地变量创建的所有内存都将被清除,但这并不意味着JVM将清除为“已生成”对象而在“永久代”中分配的内存在myMethod
如果我只声明了s并且没有初始化它,那么情况会如何呢?JVM是否仍然将内存分配给java.lang.String类的所有方法,如果是这样,为什么?
特别是在讨论String
类的时候,JVM会以“永久代”的方式为String
分配空间,JVM启动时,以及是否初始化String变量,从“永久代”的角度来看并不重要。
在讨论其他用户定义的类时,只要您定义了类,JVM就会加载类并在“永久生成”中分配内存,即使您不创建类的对象,内存也会在“永久生成” ( 非堆区 ),当你创建一个类的对象时,内存分配在“Eden Space”( 堆区 )中。
以上信息的来源和进一步阅读:
既然艾尔斯接受的答案和哈格瓦尔的答案都很清楚,只是想详细说明第四个问题:
如果我只声明了s并且没有初始化它,那么情况会如何呢?JVM是否仍然将内存分配给java.lang.String类的所有方法,如果是这样,为什么?
基本上,尽管类数据(包含字段和方法信息)存储在永久生成(JDK-8之后的元空间)中,但请注意它在java.lang中的对象是很重要的。在堆上为其分配数据的字符串类(例如保存该字符串的所有字符信息的char [])。
直到一个新的字符串对象被创建 - 不管是使用'new'关键字还是通过创建一个新的字符串文字(例如:“helloworld”),这都不会发生。
链接地址: http://www.djcxy.com/p/78761.html上一篇: Java stack and heap memory management
下一篇: Why heap needs additional memory for management while stack doesn't?