Java 메모리 관리 스택 & 힙

코딩을 합시다·2023년 3월 20일
0

Stack

  • Heap 영역에 생성된 Object 타입의 데이터의 참조값이 할당된다. #
  • 원시타입의 데이터가 값과 함께 할당된다. #
  • 지역변수들은 scope 에 따른 visibility 를 가진다. #
  • 각 Thread 는 자신만의 stack 을 가진다. #

Heap

  • Heap 영역에는 주로 긴 생명주기를 가지는 데이터들이 저장된다. (대부분의 오브젝트는 - 크기가 크고, 서로 다른 코드블럭에서 공유되는 경우가 많다) #
  • 애플리케이션의 모든 메모리 중 stack 에 있는 데이터를 제외한 부분이라고 보면 된다. #
  • 모든 Object 타입(Integer, String, ArrayList, ...)은 heap 영역에 생성된다. #
  • 몇개의 스레드가 존재하든 상관없이 단 하나의 heap 영역만 존재한다. #
  • Heap 영역에 있는 오브젝트들을 가리키는 레퍼런스 변수가 stack 에 올라가게 된다. #

이제 예시를 통해서 좀 더 알아보기 쉽게 확인해보자.

프로그램의 시작과 함께 실행되는 첫 구문은 아래와 같다.

여기서 new 키워드는 특별한 역할을 한다. 생성하려는 오브젝트를 저장할 수 있는 충분한 공간이 heap 에 있는지 먼저 찾은 다음, 빈 List 를 참조하는 listArgument 라는 로컬변수를 스택에 할당한다. 결과는 아래와 같다.

다음으로

구문이 실행되는데, 위 구문은 listArgument.add(new String("yaboong")); 과 같은 역할을 한다. 즉, new 키워드에 의해 heap 영역에 충분한 공간이 있는지 확인한 후 “yaboong” 이라는 문자열을 할당하게 된다. 이때 새롭게 생성된 문자열인 “yaboong” 을 위한 변수는 stack 에 할당되지 않는다. List 내부의 인덱스에 의해 하나씩 add() 된 데이터에 대한 레퍼런스 값을 갖게 된다. 그림으로 표현하면 아래와 같다.

listParam 이라는 참조변수가 새롭게 stack 에 할당되어 기존 List 를 참조하게 된다.
기존에 인자인 listArgument 가지고 있던 값(List 에 대한 레퍼런스)을 그대로 listParam 이 가지게 되는 것이다.
print() 함수 내부에서는 listArgument가 범위 밖에 있게 되므로 접근할 수 없는 영역이 된다.

다음으로, print() 함수 내부에서는 List 에 있는 데이터에 접근하여 값을 value 라는 변수에 저장한다. 이 때 print() 함수의 scope 에서 stack 에 value 가 추가되고, 이 value 는 listParam 을 통해 List 의 0번째 요소에 접근하여 그 참조값을 가지게 된다. 그리고나서 또 데이터를 추가하고, 출력함으로 print() 함수의 역할은 마무리 된다.

Object 타입의 데이터, 즉 heap 영역에 있는 데이터는 함수 내부에서 파라미터로 copied value 를 받아서 변경하더라도 함수호출이 종료된 시점에 변경내역이 반영되는 것을 볼 수 있다.

하지만 아래의 코드는 다른 결과를 보여준다.

Integer 도 Object 를 상속받아 구현되었으니… Object 타입이고…
당연히 20 이 나와야 하지만 10이 나온다.

이유가 뭘까?
정답은 불변객체(immutable) 로써 어떤 연산을 수행할때마다 기존 오브젝트를 변경하는 것이 아니라 새로운 오브젝트를 생성하기 때문이다.

우선 a 객체 Stack, 10 값을 Heap 영역에 할당 되고 a가 10 값을 참조하게 된다.

이후 param이 생성이 되고 같은 10 값을 참조하게 되는데

param에 10을 더할때 새로운 Inger 필드가 생겨나게 되고 기존에 참조하던 10 값이 아닌 20 값을 참조하게 된다.

이후 changeInteger가 끝남에 따라 param 이라는 변수는 스택에서 pop 된다.
20 값은 아무도 참조하지 않는 상태가 된다.

String, Integer, Character, Byte, Boolean, Long, Double, Float, Short 클래스는 모두 Immutable 이다. 그래서 heap 에 있는 같은 오브젝트를 레퍼런스 하고 있는 경우라도, 새로운 연산이 적용되는 순간 새로운 오브젝트가 heap 에 새롭게 할당된다.

다른 예시도 하나 살펴보자.

이후 Garbage Collection 이 일어나면 Unreachable 오브젝트들은 메모리에서 제거된다.
즉 기존의 "https://" 라는 문자열을 레퍼런스 하고 있는 변수는 아무것도 없으므로 Unreachable 오브젝트가 된다.

Garbage Collection 이 일어난 후의 메모리 상태는 아래와 같을 것이다.


참고 : https://yaboong.github.io/java/2018/05/26/java-memory-management/

0개의 댓글