Хранится ли массив примитивов Java в стеке или куче?

85

У меня есть такое объявление массива:

int a[];

Вот aмассив примитивного intтипа. Где хранится этот массив? Он хранится в куче или стеке? Это примитивный тип int, все примитивные типы не хранятся в куче.

user241924
источник
38
Это не массив. Это ссылка на массив. Сама ссылка может храниться в куче, если она является членом класса или объекта, или в стеке, если это локальная переменная в методе. И примитивные типы могут храниться в куче, если они являются членами класса или объекта.
uncleO

Ответы:

145

Как сказал гурукулки, хранится в куче. Тем не менее, ваш пост предполагает недоразумение, вероятно, из-за некоего благонамеренного человека, распространяющего миф о том, что «примитивы всегда живут в стеке». Это неправда. Локальные переменные имеют свои значения в стеке, но не все примитивные переменные являются локальными ...

Например, рассмотрим это:

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, возможно, экспериментальные, могут определить, когда объект никогда не «ускользнет» из метода, и могут выделить весь объект в стеке. Однако концептуально это в куче, если вы решите позаботиться.

Джон Скит
источник
1
О «анализе выхода» в Java: blog.juma.me.uk/2008/12/17/objects-with-no-allocation-overhead В нем говорится, что он присутствует с момента выпуска раннего доступа JDK 6 Update 14, и включен по умолчанию, начиная с обновления 23 для JDK 6.
Гвидо,
это что-то меняет, если массив является публичным статическим окончательным? Разве это не должно быть частью постоянного пула?
Malachiasz
@Malachiasz: Нет. Массив никогда не бывает константой.
Джон Скит
@JonSkeet: во всех известных мне версиях библиотеки Java каждая из Stringних поддерживается файлом char[]. Я считаю, что буквальные строки хранятся в общедоступном пуле констант; для оптимизации сборщика мусора это будет означать, что резервные массивы должны храниться аналогичным образом (в противном случае постоянный пул должен быть сканирован во время любого цикла сборщика мусора, где резервный массив в противном случае имел бы право на сбор).
supercat 08
@supercat: Да, в этом есть смысл. Но любой массив вы объявляете себя никогда не заканчивается, как часть постоянного бассейна.
Джон Скит
37

Он будет храниться в куче

потому что массив - это объект в java.

РЕДАКТИРОВАТЬ : если у вас есть

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

Думайте об этом коде как о сообщении компилятору: «Создайте объект массива, который будет содержать четыре целых числа, и назначьте его ссылочной переменной с именем testScores. Кроме того, продолжайте и установите каждый intэлемент в ноль. Спасибо».

ГуруКулки
источник
3
И ссылочная переменная с именем testScores (указывающая на массив в куче) будет в стеке.
Zaki
11
Код говорит «Спасибо» только в том случае, если вы предоставляете эту -gопцию компилятору. В противном случае он будет оптимизирован.
Mob
1
@mob Вы делаете предположение, что это единственный код. Вероятно, лучше предположить, что эти две строки являются частью более крупной программы, которая фактически использует массив.
Code-Apprentice
22

Это массив примитивных типов, который сам по себе не является примитивным. Хорошее практическое правило заключается в том, что при использовании ключевого слова new результат будет в куче.

Эсбен Сков Педерсен
источник
19

Я просто хотел поделиться несколькими тестами, которые я провел на эту тему.

Массив размером 10 миллионов

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 МБ, что составляет 10 м * sizeof (double).

Но если мы используем Double вместо double

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

На выходе будет 40 МБ. У нас есть только двойные ссылки, они не инициализируются.

Заполнение двойным

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();
}

Еще 40МБ. Потому что все они указывают на один и тот же объект 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 хранятся в куче.

ахерон55
источник
1
Не могли бы вы вставить деталь кода memInfo (), пожалуйста? :)
hedleyyan