Java堆栈和堆内存管理

Adi*_*lai 19 java memory memory-management heap-memory stack-memory

我想知道如何在以下程序中分配内存:

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!";

    }

}
Run Code Online (Sandbox Code Playgroud)

现在,据我所知,下图描述了内存分配的发生方式:
基本运行时内存分配


在上图中,堆栈存储器中的内存,objs实际上是对它们放置在堆内存中的" 实际对象 " 的引用.
以下是我想到的一系列问题:

  1. 哪里的方法小号存储?
  2. 如果我创建了另一个MemoryClass内部对象myMethod,JVM会在堆栈内存中再次为相同的方法分配内存吗?
  3. JVM myMethod是否会在执行完成后立即释放分配的内存,如果是,它将如何管理问题2中提到的情况(仅当JVM多次为同一方法分配内存时才适用).
  4. 如果我只声明了s并且没有初始化它,那么JVM是否仍会为所有java.lang.String类的方法分配内存,如果是这样,为什么呢?

arc*_*rcy 19

存储方法在哪里?

它们存储在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等内容; 你会通过阅读它的工作原理来获得更多,而不是用你能想到的例子来嘲笑它.


hag*_*wal 7

首先要做的事情是:我假设你的问题在阅读完这篇文章之后就出现了(因为在那里我看到的图表与你的图表非常相似)所以我不会引用或突出那里提到的任何一点,并会尝试回答用你在帖子中不那么明显的要点提问.

阅读所有问题,我的印象是你很清楚如何在堆栈和堆中分配内存,但对类的元数据有疑问,即在内存中的哪些位置,类的方法将被存储以及如何回收它们.那么,首先让我尝试解释JVM内存区域:


JVM内存区域

让我首先介绍这两个描绘JVM内存区域的图:

图表来源

在此输入图像描述

图表来源

在此输入图像描述

现在,从上面的图表中可以清楚地看到JVM内存的树形结构,我将尝试对它进行修改(@Adit:请注意,与您有关的区域是PermGen Space或非堆内存的永久生成空间) .

  • 堆内存
    • 年轻一代
      • 伊甸园空间
      • 幸存者空间
    • 老一代
      • 终身代
  • NonHeap内存
    • 永久代
    • 代码缓存(我认为HotSpot Java VM"仅限"包含)

堆内存

堆内存是运行时数据区,Java VM从中为所有类实例和数组分配内存.堆可以是固定的或可变的大小.垃圾收集器是一个自动内存管理系统,可回收对象的堆内存.

年轻一代

年轻代是创建所有新对象的地方.当年轻一代被填满时,进行垃圾收集.此垃圾收集称为Minor GC.Young Generation分为以下两部分

Eden space:最初为大多数对象分配内存的池.

幸存者空间:包含在伊甸园空间垃圾收集中幸存下来的对象的池.

老一代

旧生成内存包含多轮次要GC后长寿和存活的对象.通常垃圾收集在Old Generation内存中完成时执行.旧一代垃圾收集称为主要GC,通常需要更长的时间.老一代包含以下部分:

终身空间:包含在幸存者空间中存在一段时间的物体的池.

非堆内存

非堆内存包括在Java VM的内部处理或优化所需的所有线程和内存之间共享的方法区域.它存储每类结构,例如运行时常量池,字段和方法数据,以及方法和构造函数的代码.方法区域在逻辑上是堆的一部分,但是根据实现,Java VM可能不会垃圾收集或压缩它.与堆存储器一样,方法区域可以是固定的或可变的大小.方法区域的内存不需要是连续的.

永久世代

该池包含虚拟机本身的所有反射数据,例如类和方法对象.对于使用类数据共享的Java VM,这一代分为只读和读写区域.

代码缓存

HotSpot Java VM还包括代码缓存,其中包含用于编译和存储本机代码的内存.


特别回答OP的问题

存储方法在哪里?

非堆内存 - >永久生成

如果我在myMethod中创建了MemoryClass的另一个对象,那么JVM会在堆栈内存中再次为相同的方法分配内存吗?

堆栈内存只包含局部变量,因此新的ORV(对象引用变量)MemoryClass仍将在堆栈帧中创建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"(堆区域)中分配.


以上信息和进一步阅读的来源: