Java基元数组存储在堆栈还是堆中?


85

我有一个这样的数组声明:

int a[];

a是原始int类型的数组。该数组存储在哪里?它存储在堆还是堆栈上?这是一个原始类型int,所有原始类型都不存储在堆上。


38
那不是数组。它是对数组的引用。如果引用本身是类或对象的成员,则引用本身可以存储在堆中;如果引用是方法中的局部变量,则可以存储在堆栈中。如果原始类型是类或对象的成员,则它们可以存储在堆中。
uncleO 2010年

Answers:


143

正如gurukulki所说,它存储在堆中。但是,您的帖子提出了一个误解,可能是由于一些善意的人传播了“原始生物始终存在”的神话。这是不正确的。局部变量在堆栈上有其值,但并非所有原始变量都是局部的...

例如,考虑一下:

public class Foo
{
    int value;
}
...

public void someOtherMethod()
{
    Foo f = new Foo();
    ...
}

现在,f.value住在哪里?神话暗示它在堆栈中-但实际上它是新Foo对象的一部分,并存在于堆1中。(请注意,其f自身的值是一个引用,并存在于堆栈中。)

从那里开始,很容易进行阵列操作。您可以将数组视为很多变量-new int[3]有点像拥有这种形式的类:

public class ArrayInt3
{
    public readonly int length = 3;
    public int value0;
    public int value1;
    public int value2;
}

1实际上,它比这更复杂。堆栈/堆的区别主要是实现细节-我相信某些JVM(可能是实验的JVM)可以告诉对象何时永不“退出”方法,并可以在堆栈上分配整个对象。但是,如果您选择关心的话,它在概念上是堆的。


1
关于Java中的“逃脱分析”:blog.juma.me.uk/2008/12/17/objects-with-no-allocation-overhead 它表示自JDK 6 Update 14的早期访问版本以来就存在,并且因为JDK 6更新23.默认启用
圭多

如果数组是public static final,它会改变什么吗?那不应该是常量池的一部分吗?
玛拉基亚斯2014年

@Malachiasz:不。数组永远不是常数。
乔恩·斯基特

@JonSkeet:在我所知道的所有Java库版本中,每个库都由来String支持char[]。我相信文字字符串存储在公共常量池中;对于GC优化,这意味着应该以同样的方式存储后备阵列(否则,必须在任何GC周期内都必须扫描常量池,否则该后备阵列将有资格进行收集)。
2014年

@supercat:是的,这很有道理。但是,您声明自己的任何数组都永远不会成为常量池的一部分。
乔恩·斯基特

37

它将存储在堆中

因为array是Java中的对象。

编辑 :如果你有

int [] testScores; 
testScores = new int[4];

认为这段代码是对编译器说的:“创建一个将包含四个int的数组对象,并将其分配给名为的引用变量testScores。此外,继续将每个int元素设置为零。谢谢。”


2
并且名为testScores的引用变量(指向堆上的数组)将在堆栈上。
扎基

11
如果-g向编译器提供选项,则代码仅显示“谢谢” 。否则它将被优化掉。
暴民

1
@mob您正在假设这是唯一的代码。最好假设这两行是实际使用该数组的较大程序的一部分。
Code-Apprentice

22

它是原始类型的数组,而原始类型本身不是原始类型。一个好的经验法则是,当涉及到new关键字时,结果将在堆上。


18

我只想分享我在该主题上进行的一些测试。

1000万个数组

public static void main(String[] args) {
    memInfo();
    double a[] = new double[10000000];
    memInfo();
}

输出:

------------------------
max mem = 130.0 MB
total mem = 85.0 MB
free mem = 83.6 MB
used mem = 1.4 MB
------------------------
------------------------
max mem = 130.0 MB
total mem = 130.0 MB
free mem = 48.9 MB
used mem = 81.1 MB
------------------------

如您所见,已用堆大小增加了约80 MB,即10m * sizeof(double)。

但是如果我们使用Double而不是double

public static void main(String[] args) {
    memInfo();
    Double a[] = new Double[10000000];
    memInfo();
}

输出将显示40MB。我们只有Double引用,它们没有初始化。

用Double填充

public static void main(String[] args) {
    memInfo();
    Double a[] = new Double[10000000];      
    Double qq = 3.1d;
    for (int i = 0; i < a.length; i++) {
        a[i] = qq;
    }
    memInfo();
}

仍然是40MB。因为它们都指向同一个Double对象。

用double初始化

public static void main(String[] args) {
    memInfo();
    Double a[] = new Double[10000000];
    Double qq = 3.1d;
    for (int i = 0; i < a.length; i++) {
        a[i] = qq.doubleValue();
    }
    memInfo();
}

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

线

a[i] = qq.doubleValue();

相当于

a[i] = Double.valueOf(qq.doubleValue());

相当于

a[i] = new Double(qq.doubleValue());

由于我们每次都创建新的Double对象,因此会炸毁堆。这表明Double类内部的值存储在堆中。


1
您可以粘贴memInfo()pls的代码详细信息吗?:)
hedleyyan

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.