【问题标题】:Where does class, object, reference variable get stored in Java. In heap or in stack? Where is heap or stack located?类、对象、引用变量在 Java 中存储在哪里。在堆中还是在堆栈中?堆或堆栈位于何处?
【发布时间】:2012-11-29 11:11:45
【问题描述】:

我知道方法的变量存储在堆栈中,而类变量存储在堆中。那么我们创建的类和对象在 Java 中存储在哪里呢?

【问题讨论】:

  • 看看this article,正式定义见JVM Spec
  • 令许多人感到困惑的是对象存储在堆上,但对您在 Java 中使用的对象的引用可以在堆栈上。类似地,对 Class 对象的引用可以在堆栈上,Class 对象在堆上,但 Class 对象是关于该类的元数据,实际代码在 PermGen 中。
  • @PeterLawrey:PermGen 被认为是堆的一部分,对吧?
  • @Thilo AFAIK,它不是最大堆大小的一部分,所以我不会说。它是一个管理内存空间。

标签: java


【解决方案1】:

以下是关于 Java 中的内存分配需要考虑的几点。

注意:

对象和对象引用是不同的东西。

  1. Java 中有new 关键字经常用于创建新对象。但是new 所做的是为您正在创建的类的对象分配内存并返回一个引用。这意味着,无论何时您将对象创建为静态或本地对象,它都会存储在 heap 中。

  2. 所有类变量基元或对象引用(只是指向对象存储位置的指针,即堆)也存储在堆中。

  3. ClassLoader 加载的类和 静态变量静态对象引用 存储在堆中的一个特殊位置,该位置是永久的一代。

  4. 局部原始变量局部对象引用方法参数存储在堆栈中。

  5. 本地函数(方法)存储在堆栈中,而静态函数(方法)则永久存储。

  6. 与类相关的所有信息,如类名、与类关联的对象数组、JVM 使用的内部对象(如 Java/Lang/Object)和优化信息进入永久代区域。

  7. 要了解堆栈、堆、数据,您应该阅读操作系统中的进程和进程控制块。

【讨论】:

  • 这个答案很有帮助,但恐怕我需要一个参考链接。
  • @mudit_sen - 在声明“本地函数”时不确定您指的是什么。如果您在谈论实例方法,那么这些引用必须位于堆上。此外,方法永远不是对象结构的一部分,因为它们本身不是对象。它们作为指令代码存在于代码段及其堆上实例指向的方法的起始地址。
  • @supi - 如链接 blog.codecentric.de/en/2010/01/… 中所述,运行时数据区包含方法区。它有一个解释运行时数据区的老鼠图像。方法代码存在于该方法区中。代码段是什么意思?那是 Method Area 中内存的一部分吗?此外,当您说堆上的实例指向方法的起始地址时,这是否意味着堆中特定类的所有实例都将包含方法区域中方法的起始地址?请你澄清一下?
  • @RohitT 是的,托管堆上的类的所有实例都应该指向方法区域中相应方法代码的起始地址。关键是方法代码不会改变,无论是静态方法还是实例方法。因此,通过在每个实例中复制它来浪费内存空间是没有意义的。该方法唯一改变的是参数和返回值,顺便说一句,它们在堆栈上可用。
  • @RohitT 代码段就像一个通用术语,表示指令代码所在的区域,如方法区域。编译后,典型的类对象最终将被转换为引导代码,该代码将在非托管堆中分配特定地址和内存来存储类对象,以及初始化其静态属性的指令和驻留在方法区的方法代码每个这样的类对象(我从您分享的图表和文章中了解了这个实现细节 - 谢谢)
【解决方案2】:

Java 中的所有对象都存储在堆上。持有对它们的引用的“变量”可以在堆栈上,也可以包含在其他对象中(那么它们不是真正的变量,而是字段),这也将它们放在堆上。

定义类的类对象也是堆对象。它们包含构成类的字节码(从类文件加载),以及由此计算的元数据。

【讨论】:

    【解决方案3】:

    内存的堆栈部分包含方法、局部变量和引用变量。

    堆部分包含对象(也可能包含引用变量)。

    经过简短的 google,我找到了一个描述它的链接,是的 youtube 视频 链接。 ^_^

    http://www.youtube.com/watch?v=VQ4eZw6eVtQ

    【讨论】:

    • “堆栈包含方法”(局部变量除外)是什么意思?
    • 视频不可用。你能给我们另一个链接吗?
    • 该视频不再可用。下一次,考虑在你的答案中总结视频。这样,如果视频不可用,它不会对您的回答产生太大影响。
    • 堆栈不包含方法。
    • @Kent - 堆栈部分不包含这样的方法,仅包含当前正在执行的方法的数据(如局部变量、临时结果等)。实际的方法指令代码进入方法区域而不是堆栈。
    【解决方案4】:

    这个概念很简单:

    1. 实例变量(原始、包装类、引用、对象(非静态))- 堆
    2. 局部变量,引用 - 堆栈
    3. 其他数据对象,例如:类元数据、JVM 代码、静态变量、静态对象引用、静态函数等,以前位于 Permgen 空间(Java 7 之前),现在在 JAVA 8 中被移动到元空间。

    PS : Metaspace 是本机内存的一部分,因此现在无需担心 OOM:Pergem Exeption。

    更多详情:https://siddharthnawani.blogspot.com/

    【讨论】:

      【解决方案5】:

      局部变量(方法变量)+方法存在于堆栈中。虽然对象及其实例变量存在于堆中。

      现在,引用变量可以是局部变量(如果在方法内部创建)或实例变量(如果在类内部创建,但在方法外部创建)。所以引用变量可以在任何地方,堆栈或堆。

      【讨论】:

      • 方法不在堆栈中。
      【解决方案6】:

      根据 JVM 规范,

      类和它自己的常量池,即静态变量存储在方法区中。 这里的类只是一堆字段,方法和常量,这些方法以指令的形式存储在方法区中,可以通过地址来识别。

      Objects只不过是一个填充类模板,将在Heap Area中创建,但对象引用是在Stack中创建的。

      public class Sample{
      int field;
      static int constant;
      public void test(){
      int localVariable=10;
      Sample samp=new Sample();
        }
      }
      

      在示例中, sample.class 将进入方法区,即'field'、'constant'和方法'test'被分配在方法区。

      当开始执行时, new Sample() 创建的对象将进入 Heap 但“samp”只是一个对象引用,它进入堆栈并保存存在于

      欲了解更多信息,请查看此链接, JVM Specification

      【讨论】:

      • "field" 将存储在堆而不是方法区域,因为它既不是运行时常量也不是静态变量。
      【解决方案7】:

      JVM中的运行时数据区可以划分如下,

      1. 方法区: 已编译类文件的存储区。 (每个 JVM 实例一个)

      2. 堆: 对象的存储区域。 (每个 JVM 实例一个)

      3. Java 堆栈: 局部变量、中间操作结果的存储区域。 (每个线程一个)

      4. PC 寄存器: 如果下一条指令是本机方法,则存储要执行的下一条指令的地址,则 pc 寄存器中的值将是未定义的。 (每个线程一个)

      5. 本机方法堆栈:帮助执行本机方法(用 Java 以外的语言编写的方法)。 (每个线程一个)

      【讨论】:

        【解决方案8】:

        在 Java 中,变量可以保存对象引用(sString s="OOO" 中保存对对象"OOO" 的引用)或原始值(iint i=10 中保存10)。这些变量可以在堆上也可以在栈上,这取决于它们是否是本地的以及您使用的是哪种类型的 JVM。

        在 Java Virtual Machine 8 规范文档 (https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html) 中,与此相关的内存组织如下。

        1. 每个 Java 线程都有自己的 JVM 堆栈。

          它保存局部变量和部分结果,并在 方法调用和返回。

          堆栈中也可能存在对象引用。 该 规范不区分原始变量 和一个引用变量,只要它是本地的。此堆栈用于使用 称为帧的数据单位。不过,

          因为 Java 虚拟机堆栈永远不会被直接操作 除了推送和弹出帧,帧可能是堆分配的。

          因此,框架所在的位置取决于 JVM 规范(如 OpenJDK 或 Oracle)的实现。

        2. 每个 JVM 实例都有一个堆。堆是

          ... 运行时数据区域,所有类实例和内存都来自该区域 数组已分配。

          保存对象数据的实际内存驻留在堆上。这 堆在所有 JVM 线程之间共享。这也包括对象 引用,它们是对象的一部分,即实例变量。

          例如,参加以下课程。

          class Moo{ private String string= "hello"; } class Spoo{ private Moo instanceReferenceVariable = new Moo(); public void poo(){ int localPrimitiveVariable=12; Set<String> localReferenceVariable = new HashSet<String>(); } }

          这里是变量instanceReferenceVariable引用的对象
          将在堆上,因此该对象的所有实例变量,例如 string,也将在堆上。变量localPrimitiveVariablelocalReferenceVariable 将在堆栈中。

        3. 方法区:方法区是堆的一个受限部分

          ... 存储每个类的结构,例如运行时常量池, 字段和方法数据,以及方法和构造函数的代码, 包括类和实例中使用的特殊方法 初始化和接口初始化。

        4. 运行时常量池:这是方法区的一部分。

        希望这能够澄清事情。

        【讨论】:

          猜你喜欢
          • 2016-09-08
          • 2015-05-27
          • 2010-10-26
          • 2010-11-15
          • 2015-01-24
          • 2021-06-19
          • 2015-03-13
          • 2014-04-21
          • 1970-01-01
          相关资源
          最近更新 更多