Как Java хранит примитивные типы в ОЗУ? [дубликат]

этот вопрос уже есть ответ здесь:


речь идет не о том, идут ли примитивы в стек или кучу, а о том, где они сохраняются в фактической физической ОЗУ.


взять простой пример:

int a = 5;

Я знаю 5 хранится в блоке памяти.

моя область интересов-где хранится переменная "a"?

связанные с этим дополнительные вопросы: где это происходит, когда "a" связывается с блоком памяти, который содержит примитивное значение 5? Есть ли другой блок памяти, созданный для хранения "a"? Но это будет выглядеть так, как будто a является указателем на объект, но это примитивный тип, участвующий здесь.

2 ответов


подробно примитивы Java идут в стек или кучу? -

допустим у вас есть функция foo():

void foo() {
   int a = 5;
   system.out.println(a);
}

затем, когда компилятор компилирует эту функцию, он создаст инструкции байт-кода, которые оставляют 4 байта места в стеке всякий раз, когда эта функция вызывается. Имя "a" полезно только вам-компилятору, он просто создает для него место, запоминает, где это место находится, и везде, где он хочет использовать значение вместо этого он вставляет ссылки на место памяти, зарезервированное для этого значения.

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

когда функция main вызов другой функции foo, вот один пример того, что может произойти (здесь есть пара упрощающих белых лжи):

  • main хочет передать параметры foo. Он выталкивает эти значения в верхнюю часть стека таким образом, что foo будет точно знать, где они будут помещены (main и foo передаст параметры в последовательном путь.)
  • main нажимает адрес, куда должно вернуться выполнение программы после foo сделано. Это увеличивает указатель стека.
  • main звонки foo.
  • , когда foo запускается, он видит, что стек в настоящее время находится по адресу X
  • foo хочет выделить 3 int переменные в стеке, поэтому ему нужно 12 байтов.
  • foo будет использовать X + 0 для первого int, X + 4 для второго int, X + 8 для третий.
    • компилятор может вычислить это во время компиляции, и компилятор может полагаться на значение регистра указателя стека (ESP в системе x86), и поэтому код сборки, который он записывает, делает такие вещи, как "хранить 0 в адресе ESP + 0", "хранить 1 в адресе ESP + 4" и т. д.
  • параметры main нажал на стеке перед вызовом foo и по foo путем вычисления некоторого смещения от стека указатель.
    • foo знает, сколько параметров он принимает (скажем, 3), поэтому он знает, что, скажем, X - 8 - первый, X - 12-второй, а X-16-третий.
  • теперь foo имеет место в стеке, чтобы сделать свою работу, он делает это и заканчивает
  • перед main под названием foo, main написал свой обратный адрес в стеке перед увеличением указателя стека.
  • foo ищет адрес чтобы вернуться к-скажем, что адрес хранится в ESP - 4 - foo смотрит на это место в стеке, находит там обратный адрес и переходит к обратному адресу.
  • теперь остальная часть кода в main продолжает работать, и мы сделали полную поездку туда и обратно.

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

я знаю, что это объяснение немного размывает линию между x86 и java, но я надеюсь, что это поможет проиллюстрировать, как на самом деле работает оборудование.

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

что происходит, когда два вызова функций хотят иметь дело с одним и тем же фрагментом памяти, независимо от того, в каком потоке они находятся или где они находятся в стеке?

вот где приходит куча. Обычно (но не всегда) одна программа имеет ровно одну кучу. Куча называется кучей, потому что это просто большая куча памяти.

использовать память в куче, вы должны вызвать процедуры распределения-процедуры, которые находят неиспользуемое пространство и дают его вам, и процедуры, которые позволяют вам вернуть пространство, которое вы выделили, но больше не используете. Распределитель памяти получает большие страницы памяти из операционной системы, а затем раздает отдельные маленькие биты тому, что ему нужно. Он отслеживает, что ОС дала ему, и из этого, что она дала остальной части программы. Когда программа запрашивает память кучи, она ищет самый маленький кусочек памяти, который у него есть, который соответствует потребностям, помечает этот кусок как выделяемый и передает его обратно остальной части программы. Если у него больше нет свободных кусков, он может попросить операционную систему больше страниц памяти и выделить оттуда (до некоторого предела).

в таких языках, как C, те процедуры выделения памяти, которые я упомянул, обычно называются malloc() попросить памяти и free(), чтобы вернуть его.

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

Итак, теперь мы знаем, что память выделяется в куче или в стеке, что происходит, когда я создаю закрытую переменную в классе?

public class Test {
     private int balance;
     ...
}

откуда берется это воспоминание? Ответ-куча. У вас есть код, который создает новый


я думаю, я понял, что вы не хотите спрашивать, хранятся ли данные в куче или стеке! у нас одна и та же загадка!

вопрос, который вы задали, очень связан с языком программирования и тем, как операционная система имеет дело с процессом и переменными.

это очень интересно, потому что, когда я был в своем университете, изучая C и c++, я сталкиваюсь с тем же вопросом, что и вы. после прочтения некоторых ASM код, составленный GCC, у меня есть немного о понимании с этим, давайте обсудим об этом, если какая-либо проблема, прокомментируйте ее и позвольте мне узнать больше об этом.

на мой взгляд, имя переменной не будет сохранено, а значение переменной будет сохранено, потому что в ASM код, нет реального variable name за исключением cache name короче говоря, вся так называемая переменная - это просто off set С stack или heap.
что, я думаю, является подсказкой для моего обучения, так как ASM интернет-с имени переменной, другие язык может иметь ту же стратегию.
Они просто хранят off set для реального места для хранения данных.
приведем пример, скажем переменную name a расположенный по адресу @1000 типа этого a является целым числом, таким образом, в памяти address

addr  type     value
@1000 int      5    

что @1000-это off set где хранятся реальные данные.

как вы можете видеть, что данные помещаются в реальном off set для этого.
В моем понимании процесса, что все переменная будет заменена на " адрес "этой" переменной "в начале процесса, что означает, что в то время как CPU имеет дело только с" адресом", который уже выделен в памяти.
давайте еще раз рассмотрим эту процедуру: что вы определили
int a=5; print(a);
после компиляции программа переносится в другой формат (все по моему воображению):

stack:0-4  int  5
print stack:0-4

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

@2000 4  5     //allocate 4 byte from @2000, and put 5 into it
print @2000 4  //read 4 byte from @2000, then print

поскольку память процесса выделяется CPU,@2000 это off set этого имени переменной, что означает name будет заменен только адресом памяти, затем будет читать данные 5 с этого адреса, а затем выполнить команду печати.

переосмысливать

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