Подтвердить что ты не робот

Использование памяти Java int

Пока я размышлял об использовании памяти различных типов, я начал немного путать с тем, как Java использует память для целых чисел при передаче методу.

Скажем, у меня был следующий код:

public static void main (String[] args){
     int i = 4;
     addUp(i);
}

public static int addUp(int i){
     if(i == 0) return 0;
     else return addUp(i - 1);         
}

В этом следующем примере мне интересно, правильна ли следующая логика:

  • Сначала я сделал память для целого числа я = 4. Затем передаю его методу. Однако, поскольку примитивы не указаны в Java, в addUp (i == 4), я создаю другое целое число я = 4. Затем после этого есть еще один addUp (i == 3), addUp (i == 2), addUp (i == 1), addUp (i == 0), в котором каждый раз, поскольку значение не указано, в памяти выделяется новое значение i.
  • Затем для одного значения "int i" я использовал 6 целых значений.

Однако, если бы я всегда проходил его через массив:

public static void main (String[] args){
     int[] i = {4};
     // int tempI = i[0];
     addUp(i);
}

public static int addUp(int[] i){
     if(i[0] == 0) return 0;
     else return addUp(i[0] = i[0] - 1);         
}

- Поскольку я создаю целочисленный массив размера 1, а затем передаю это в addUp, который будет снова передан для addUp (i [0] == 3), addUp (i [0] == 2), addUp (i [0] == 1), addUp (i [0] == 0), мне пришлось использовать только одно целое пространство памяти массива и, следовательно, гораздо более экономически выгодно. Кроме того, если бы я заранее запустил значение int, чтобы сохранить начальное значение я [0], у меня все еще есть свое "оригинальное" значение.

Затем это приводит меня к вопросу: почему люди передают примитивы, такие как int в Java-методах? Разве не намного эффективнее память, чтобы просто передать значения массива этих примитивов? Или это первый пример как-то еще только O (1) памяти?

И в дополнение к этому вопросу, я просто удивляюсь различиям в памяти при использовании int [] и int, особенно для размера 1. Спасибо заранее. Я просто задавался вопросом, насколько эффективна память с Java, и это пришло мне в голову.

Спасибо за ответы! Я просто сейчас быстро задаюсь вопросом, должен ли я "анализировать" большую память о каждом коде, будут ли они оба считаться O (1) или ошибочно предположить?

4b9b3361

Ответ 1

Что вам здесь не хватает: значения int в вашем примере идут на stack, а не на куча.

И гораздо меньше накладных расходов на использование примитивных значений фиксированного размера, существующих в стеке, по сравнению с объектами в куче!

Другими словами: использование "указателя" означает, что вам нужно создать новый объект в куче. Все объекты живут на куче; нет стека для массивов! И объекты становятся подверженными сборке мусора сразу же после их прекращения их использования. С другой стороны, стеки приходят и уходят, когда вы вызываете методы!

Помимо этого: имейте в виду, что абстракции, которые предоставляют нам языки программирования, помогают нам писать код, который легко читать, понимать и поддерживать. Ваш подход в основном заключается в том, чтобы сделать какую-то тонкую настройку, что приводит к более сложному коду. И это не так, как Java решает такие проблемы.

Смысл: с Java, реальная "магия производительности" происходит во время выполнения, когда компилятор "точно в момент" запускается! Видите ли, JIT может встроить вызовы для небольших методов, когда метод вызывается "достаточно часто". И тогда становится еще важнее больше, чтобы данные "закрывались" вместе. Как и в случае: когда данные хранятся в куче, вам может потребоваться доступ к памяти для получения значения. В то время как элементы, находящиеся в стеке, все еще могут быть "закрыты" (как в кеше процессора). Поэтому ваша небольшая идея оптимизировать использование памяти может фактически замедлить выполнение программы на порядки. Потому что даже сегодня есть порядка между доступом к кешу процессора и чтением основной памяти.

Короче говоря: избегайте попадания в такую ​​ "микро-настройку" для производительности или использования памяти: JVM оптимизирован для "обычных, типичных" вариантов использования. Поэтому ваши попытки внедрить умные обходы могут легко привести к "менее хорошим" результатам.

Итак - когда вы беспокоитесь о производительности: делайте то, что делают все остальные. И если вы один действительно уход, то узнайте, как работает JVM. Как оказалось, даже мои знания немного устарели - поскольку комментарии подразумевают, что JIT может встроить объекты в стек. В этом смысле: сосредоточьтесь на написании чистого, элегантного кода, который решает проблему прямолинейно!

Наконец: в какой-то момент это может измениться. Есть идеи ввести объекты истинной ценности в java. Которые в основном живут на стеке, а не на куче. Но не ожидайте, что это произойдет до Java10. Или 11. Или... (я думаю, this будет иметь значение здесь).

Ответ 2

Несколько вещей:

Прежде всего, будут разбиваться волосы, но когда вы передаете int в java, вы выделяете 4 байта в стек, а когда вы передаете массив (потому что это ссылка), вы фактически выделяете 8 байтов (при условии, что x64 архитектура) в стек, плюс дополнительные 4 байта, которые хранят int в кучу.

Что еще более важно, данные, которые живут в массиве, выделяются в кучу, тогда как ссылка на сам массив распределяется в стеке, при передаче целого числа не требуется выделение кучи, чтобы примитив был выделен только в стек, Со временем сокращение распределений кучи будет означать, что сборщик мусора будет иметь меньше вещей для очистки. В то время как очистка стековых фреймов тривиальна и не требует дополнительной обработки.

Тем не менее, это все спорно (имхо), потому что на практике, когда у вас есть сложные наборы переменных и объектов, которые вы, вероятно, будет в конечном итоге сгруппировать их вместе в классе. В общем, вы должны писать, чтобы повысить удобочитаемость и ремонтопригодность, а не пытаться сжать каждую последнюю производительность из JVM. JVM довольно быстро, как есть, и всегда существует Закон Мура в качестве блокиратора обратного хода.

Было бы сложно проанализировать Big-O для каждого, потому что для того, чтобы получить истинную картину, вам придется учитывать поведение сборщика мусора, и это поведение сильно зависит как от самой JVM, так и от любой среды выполнения (JIT), которые JVM сделал для вашего кода.

Помните Дональд Кнут мудрые слова о том, что "преждевременная оптимизация - корень всего зла"

Напиши код, который позволяет избежать микронастройки, код, который повышает удобочитаемость и удобство обслуживания, в долгосрочной перспективе улучшится.

Ответ 3

Если ваше предположение состоит в том, что аргументы, передаваемые функциям, обязательно потребляют память (что является ложью кстати), то в вашем втором примере, который передает массив, делается копия ссылки на массив. Эта ссылка может фактически быть больше, чем int, она вряд ли будет меньше.

Ответ 4

Независимо от того, берут ли эти методы O (1) или O (N), зависит от компилятора. (Здесь N - значение i или i[0], в зависимости.) Если компилятор использует оптимизацию хвостовой рекурсии, тогда пространство стека для параметров, локальных переменных и обратного адреса может быть повторно использовано, а реализация будет равна O (1) для пространства. Отсутствующая оптимизация хвостовой рекурсии, сложность пространства такая же, как и временная сложность, O (N).

В основном оптимизация хвостовой рекурсии (в данном случае) компилятору переписывает ваш код как

public static int addUp(int i){
     while(i != 0) i = i-1 ;
     return 0;        
}

или

public static int addUp(int[] i){
     while(i[0] != 0) i[0] = i[0] - 1 ;
     return 0 ;
}

Хороший оптимизатор может дополнительно оптимизировать петли.

Насколько я знаю, никакие компиляторы Java не реализуют оптимизацию хвостовой рекурсии в настоящее время, но нет технических причин, по которым это не может быть сделано во многих случаях.

Ответ 5

Собственно, когда вы передаете массив как параметр методу - ссылка на этот массив передается под капотом. Сам массив хранится в куче. И эта ссылка может быть 4 или 8 байт (в зависимости от архитектуры ЦП, реализации JVM и т.д., Даже больше, JLS ничего не говорит о том, насколько велика ссылка находится в памяти).

С другой стороны, примитивное значение int всегда потребляет только 4 байта и находится в стеке.

Ответ 6

Когда вы передаете массив, содержимое массива может быть изменено методом, который получает массив. Когда вы передаете примитивы int, эти примитивы не могут быть изменены методом, который их получает. Поэтому иногда вы можете использовать примитивы и иногда массивы.

Кроме того, в общем, в программировании на Java вы предпочитаете читаемость и позволяете оптимизацию памяти такого рода компилятором JIT.

Ответ 7

Ссылка на массив массивов фактически занимает больше места в кадрах стека, чем int-примитив (8 байт против 4). Вы фактически используете больше места.

Но я думаю, что основная причина, по которой люди предпочитают первый путь, состоит в том, что она понятна и понятна.

Люди действительно делают вещи намного ближе ко второму, когда задействовано больше ints.