[Java/Kotlin] JVM의 메모리 사용 방식

Jay·2020년 12월 18일
0

Java&Kotlin

목록 보기
2/30
post-thumbnail

JVM(Java Virtual Machine) 특징

  • Stack 기반의 가상 머신
  • Garbage Collection
  • 플랫폼에 독립적

JVM의 메모리 사용 방식

프로그램 실행 시, 저장장치에 있던 내용들이 메모리에 올라간다.
메모리에 어떤 데이터들이 어떤 방식으로 올라가는지 이해하는 것도 기본이라 생각한다.
메모리 구조를 알아야, 클래스 멤버, GC, 스레드가 어떤식으로 독립적으로 운영되면서 공유하는 자원이 있는지에 대한 이해를 훨씬 더 쉽고 깊게 알 수 있다.

메모리는 위의 사진과 같이 4가지 영역으로 나뉜다.
주로 우측의 static, stack, heap영역을 위주로 알면 된다.
이 세 가지 영역을 T 메모리라고 부른다.

static 영역 (메소드 영역)

  • 패키지나 클래스 정보가 올라간다.
  • 패키지나 클래스는 프로그램 시작과 동시에 모두 올라가는 것이 아니라, 실제 호출 될 때 올라간다.
  • static이란 키워드를 붙여서 선언된 필드와 메소드인 '클래스 멤버' 도 static영역이다. static영역에 있는 것은 어떤 곳에서나 접근이 가능해서 '전역' 이라는 키워드를 사용한다.
  • class영역 혹은 method 영역이라고 불린다'
  • static 영역에 자리잡게되면 JVM이 종료 될 때까지 사라지지 않고 static(고정된) 상태로 유지된다.
  • 클래스 멤버 변수의 이름, 데이터 타입, 접근 제어자 정보같은 필드 정보
  • 메소드의 이름, 리턴 타입, 파라미터, 접근 제어자 정보같은 메소드 정보
  • Type정보(Interface인지 class인지), Constant Pool(상수 풀 : 문자 상수, 타입, 필드, 객체 참조가 저장됨), static 변수, final class 변수 등

stack 영역

  • 여는 중괄호 '{'를 만날 때 마다 스택 프레임이 쌓이고, 닫는 중괄호 에 스택프레임이 사라진다. if, 반복문, 예외처리 try/catch에서 모두 스택 프레임이 생기게 된다.
  • stack 내부에서 선언된 지역변수는 stack영역에 올라간다.
  • 기본형 타입 변수의 값들은 stack에 저장되고 참조형 타입 변수는 참조값만 저장된다. (참조값은 heap영역에 존재하는 인스턴스이다.=인스턴스 주소값)
  • 외부 스택 프레임에서는 내부 스택 프레임의 변수에 접근하는 것은 불가능하나 그 반대는 가능하다.
  • 메서드를 호출하는 것은 별개의 스택프레임이기에 스택 프레임을 넘어서 접근 할 수 없다.
  • 스레드도 stack영역에 생기게 된다. 하나의 스레드는 내부적으로 별개의 T메모리 구조 static, stack, heap 영역을 갖게 된다. 이런 이유로 하나의 스레드는 다르 스레드로 접근 할 수 없지만, static 영역과 heap영역을 공유해서 사용할 수 있는 특징을 갖게 된다. (이런 이유로 멀티 프로세스 구조보다 멀티 스레드 구조가 메모리를 적게 사용할 수 있는 것이다.)

지역 변수, 파라미터, 리턴 값, 연산에 사용되는 임시 값등

heap 영역

  • 생성된 객체(인스턴스)들이 올라간다.
  • 인스턴스 필드들은 heap영역에 올라간다. (이런 이유로 static한 메소드에서 인스턴스 멤버를 접근할 수 가 없다. 어떤 인스턴스 인지도 모르고 존재하는지 여부도 모르기 때문이다.)
  • 메서드들은 static이 아니더라도 굳이 heap에 생기지 않는다. 어차피 같은 로직의 메소드이기 때문에 여러 개 일 필요 없다.
  • stack 영역에서 참조 값을 이용하여 참조형 변수가 heap영역에 있는 인스턴스를 가르켜 제어 할 수 있다.
    ex) Person a = new Person() 이라면 Person a는 stack 영역이지만 new로 생성된 Person클래스의 인스턴스는 heap 영역이다.
  • 어떤 참조 변수도 힙 영역에 있는 인스턴스를 참조하지 않게 된다면, GC에 의해 메모리에서 사라지게 된다.
  • 상속을 이용한 인스턴스를 만들었다면 상위 클래스들의 인스턴스들도 같이 생성된다. (최상위 object까지)
  • 메소드 영역에 로드된 클래스만 생성이 가능하고 Garbage Collector가 참조되지 않는 메모리를 확인하고 제거하는 영역이다.
  • new 키워드로 생성된 객체와 배열이 생성되는 영역이다.

Heap Area & Garbage Collector

🙌 GC의 주요 대상은 Heap 영역이지만 Stack과 Static영역도 그 대상이다.

힙 영역을 조금 더 구체적으로 알아보자.


위의 그림과 같이 분홍색 부분인 heap 영역의 크게 4개의 영역으로 나뉘어진다. (Eden, S0, S1, Tenured)
굳이 4개의 영역으로 나눈 이유는 GC를 더 효과적으로 하기 위함이라고 한다.

🙋‍♂️ 간단하게 GC가 일어나는 프로세스를 알아보자.

GC는 위에 보이는 것처럼 young & old generation으로 나뉜다.
이를 minor & marjor라고 부르기도 한다.

  • Minor GC : Young generation에서 일어나는 GC

    1. 최초에 객체가 생성되면 EDEN 영역에 생성된다.
    2. EDEN 영역에 객체가 가득차게 되면 첫 번쨰 GC가 일어난다.
    3. S0 영역에 EDEN 영역의 메모리를 그대로 복사한다. 그리고 S0 영역을 제외한 다른 영역의 객체를 제거한다.
    4. EDEN 영역도 가득차고 S0 영역도 가득찬다면, EDEN 영역에 생성된 객체와 S0 영역에 생성된 객체 중 참조되고 있는 객체가 있는지 검사한다.
    5. 참조 되고 있지 않은 객체는 내버려두고 참조 되고 있는 객체만 S1 영역에 복사한다.
    6. S1 영역을 제외한 다른 영역의 객체들을 제거한다.
    7. 위의 과정 중 일정 횟수 이상 참조되고 있는 객체들을 S1에서 Old generation영역으로 이동시킨다.
      -> 위의 과정을 계속 반복하며, S1영역이 꽉차기전에 계속해서 Old generation으로 비운다.
  • Major GC(Full GC) : Old generation에서 일어나는 GC
    1. Old generation에 있는 모든 객체들을 검사하며 참조되고 있는지 확인한다.

    1. 참조되지 않은 객체들을 모아 한번에 제거한다.
      -> Minor GC보다 시간이 훨씬 많이 걸리고 실행 중에 GC를 제외한 모든 쓰레드가 중지된다.

🤷 왜 Major GC는 실행 중에 모든 쓰레드를 중지 시킬까?

Major GC(FULL GC)가 발생하면, Old generation에 있는 참조가 없는 객체들을 표시하고 해당 객체들을 모두 제거한다.
그러면서 Heap 메모리 영역에 제거 되고 빈 메모리 공간이 생기는 데 이를 메우기 위해 재구성을 하게 된다. (디스크 조각 모음처럼)
그래서 메모리를 옮기고 있는데 다른 쓰레드가 메모리를 사용하면 안되기에 모든 쓰레드를 중지 시킨다.


📄 Reference

profile
Android Developer - Come to my medium (https://medium.com/@wodbs135)

0개의 댓글