堆栈和永久空间的区别

41
  • Java内存空间(Perm Space、Stack空间、Heap空间)有哪些不同之处?
  • JVM在什么时候使用其中一个或另一个?
  • 如果我使用Scala/Groovy等语言,是否存在差异?

3
好的,我会尽力为您进行翻译。以下是需要翻译的内容:相关链接:https://dev59.com/YG445IYBdhLWcg3wdqEL、https://dev59.com/i3M_5IYBdhLWcg3wq1GF。Perm Space(持久代)和 Heap Space(堆内存)是 Java 虚拟机中的两个不同的内存区域。Perm Space 用于存储类和方法相关的元数据,如类名、方法名、访问修饰符等信息。Heap Space 则用于存储对象实例的数据。在 JDK8 及以前的版本中,Perm Space 的大小是固定的,而 Heap Space 的大小可以通过命令行参数进行调整。从 JDK8 开始,Perm Space 被移除,而这些元数据则被移到了 Heap Space 中的 Metaspace 中。因此,在 JDK8 及以后的版本中,只需调整 Heap Space 的大小即可。 - Mat
1
堆栈是一种实现细节。对于Java来说可能不那么重要,因为它缺乏用户定义的值类型,但对于所有在垃圾回收语言中工作的人来说,这仍然是一篇重要的阅读材料。 - user395760
其他JVM语言使用相同的标准吗? - caarlos0
1
值得注意的是,并非所有的JVM都使用PermSpace。 - Ian McLaird
所有JVM语言都使用相同的标准,与语言本身无关,它就是JVM!它被分为堆空间和栈空间,堆空间又被分为年轻代、老年代和永久代。 - Angel O'Sphere
2个回答

90

简单地说:

  • 堆空间(Heap space):所有存活的对象都在这里分配。
  • 栈空间(Stack space):用于存储方法调用或变量实例化中变量的对象引用。
  • 永久代空间(Perm space):存储加载的类信息。

例如:

Student std = new Student();

执行上述代码后,内存状态将会如下:

  • Heap(堆):存储“new Student()”
  • Stack(栈):存储有关“std”的信息
  • Perm Space(永久代):存储Student类的信息

12
栈也存储原始字面量。 - Daniel C. Sobral
@Kowser:普通的Java对象,特别是已经interned的字符串,也会在各种垃圾回收阶段中漫游到永久代。对象越老,就越向永久代移动,最终驻留在永久代中。 - Angel O'Sphere
1
@Daniel,抱歉我要挑剔一下,java.util.Stack中的Satastructure Stack与JVM中的“堆栈”或实际计算机中的堆栈完全没有关系,除了它是一个LIFO结构之外。所以再次强调:在JVM的堆栈上什么都没有存储。堆栈只用于向被调用方法传递参数,仅此而已。也许看一下字节码的工作原理和JVM的规范,或者看一下真正处理器的汇编程序,会有助于理解这一点。Kowser:谢谢你提供的链接;D我知道GC是如何工作的,年轻时我自己实现过几个。不过这是个好链接。 - Angel O'Sphere
2
@Angel 我不是在谈论java.util.Stack,我是在谈论stack。如果东西没有存储在堆栈中,你怎么能从堆栈中检索它呢?如果你想讲汇编语言而不是堆栈概念,i386上的push操作码会将数据存储在堆栈中--它会调整一个寄存器,然后将数据写入指向它的内存位置。这个写入内存的行为在一些汇编语言(如6502)中被称为store。我开始觉得你对“store”这个词有一些特殊的定义。 - Daniel C. Sobral
1
@Angel 回到 JVM,当它执行操作码 iconst_1 时,您认为会发生什么? - Daniel C. Sobral
显示剩余7条评论

15

很抱歉在这么老的问题上添加答案 - 目前的答案不错,但由于静态代码和Java 8更新,它错过了一些边缘情况。

概述

  • 堆栈
    • 每个线程分配一个
    • 存储本地引用和基元类型
    • 这是有作用域的内存 - 当方法或线程结束时,栈中的所有数据都会丢失
    • 具有最快的访问速度,因此本地基元类型比本地对象更快
    • 所有已分配的对象实例都存在于此处
    • 分为不同代,最年轻的代是垃圾回收首先查找的地方
    • 可供所有线程使用,因此应同步进行分配和释放
    • 这种内存可能会变得分散 (但通常您不需要自己管理它)
  • PermGen
    • 存储加载的类信息
    • 存储不可变信息 (基本类型、内部化字符串)
    • 存储静态类成员

示例代码

public class SimpleVal { //The Class (loaded by a classloader) is in the PermGen

    private static final int MAGIC_CONSTANT = 42; //Static fields are stored in PermGen
    private static final SimpleVal INSTANCE = new SimpleVal(1); //Static field objects are created in the heap normally, with the reference in the PermGen ('class statics' moved to the heap from Java 7+)
    private static SimpleVal previousInstance; //Mutable static fields also have their reference in PermGen so they can easily cause memory leaks

    private int value; //Member variables will be part of the heap

    public SimpleVal(int realValue) {
        value = realValue;
        ...
    }

    public static int subtract(SimpleVal val1, SimpleVal val2) {
         ....
    }

    public int add(SimpleVal other) { //Only one copy of any method (static or not) exists - in PermGen
         int sum = value + other.value; //Local values in methods are placed in the Stack memory
         return sum;
    }

}

public static void main(String[] args) {

    SimpleVal val1 = null;
    SimpleVal val2 = new SimpleVal(3); //Both of these variables (references) are stored in the Stack 

    val1 = new SimpleVal(14); //The actual objects we create and add to the variables are placed in the Heap (app global memory, initially in the Young Gen space and later moved to old generation, unless they are very large they can immediately go old gen)

    int prim = val1.add(val2); //primitive value is stored directly in the Stack memory
    Integer boxed = new Integer(prim); //but the boxed object will be in the heap (with a reference (variable) in the Stack)

    String message = "The output is: "; //In Java 7+ the string is created in the heap, in 6 and below it is created in the PermGen
    System.out.println(message + prim);

}

Java 8 Note: PermGen space已被称为Metaspace。它的功能仍然相同,但可以自动调整大小-默认情况下,Metaspace会在本机内存中自动增加其大小,直到达到最大值(在JVM参数中指定),而PermGen始终具有固定的最大大小,与堆内存相邻。

Android Note: 从Android 4.0(实际上从3.0)开始,Android应该遵守所述的内存合同,但在旧版本中,实现出现了问题。在Android-Davlik中,“Stack”内存实际上是基于寄存器的(指令大小和计数在两者之间有所不同,但对开发人员来说功能保持不变)。

最后,有关此主题的更多信息,我在StackOverflow上看到的最好的答案是这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接