Высокоуровневые языки со сборщиками мусора, конечно, просты в использовании, но не дают понять сути происходящего. В этом их минус. А суть в работе с памятью такова:
Любой объект ненулевой длины, любая переменная - всё требует место в памяти. Это в любом языке. Это фундамент мироздания.
Вопрос только в том, как язык эту память выделяет или дает выделять программисту.
Когда вы объявляете глобальную переменную, память под нее выделяется при старте программы. Это делается операционной системой в процессе загрузки программы. Управлять этим процессом можно, но новичку лучше пока голову не забивать. Далее. Переменная внутри функции. Память под такую переменную выделяется на стеке в момент вызова функции и освобождается по завершении функции. Тут тоже программист особо на этот процесс не влияет, хотя и может. Есть понимание, что такое стек? На всякий случай вкратце - это специальный участок памяти приложения (обычно 4 мегабайта на поток) как раз для переменных внутри функций. Т.о. если рекурсивная функция вошла в бесконечную рекурсию, она очень быстро выжирает всю память стека и приложение падает.
Все что было до этого, характерно для большинства языков, в том числе и для Java.
Наконец, самое сложное - переменные и данные, под которые программист явно запрашивает память.
В java есть оператор new. В C++ он тоже есть. Они делают одно и тоже - выделяют где-то там (пока что не важно где) место под объект и инициализируют этот объект. А теперь о различии. Это основное отличие языка C++ от языков со сборщиком мусора. В C++ вы должны созданный таким образом объект явно удалить оператором delete. Если вы этого не сделаете, никто за вас это не сделает. Если этого не делать вообще, то приложение просто выжрет всю память и в итоге упадет. В Java вы просто оставляете объект на произвол судьбы и сборщик мусора сам удалит его, когда убедится, что объект больше нигде не используется.
Вот такое управление памятью.
Всякие malloc/free и т.п. оставлю без внимания, потому что это больше к C относится. Лучше пока их не трогать.