Java 최적화 - Chapter6

이유진·2024년 2월 17일
post-thumbnail

GC(Garbage Collection)

  • 메모리 관리는 컴파일러나 런타임의 영역
  • 런타임이 대신 객체를 추적하여 쓸모없는 객체를 알아서 제거해준다
  • 자동 회수한 메모리는 깨끗이 비우고 재활용 할 수 있다

GC의 기본원칙

  • 알고리즘은 반드시 모든 가비지를 수집해야한다
  • 살아있는 객체는 절대로 수집하면 안된다 (중요🌟)

Mark and Sweep 알고리즘

  • GC의 기초 알고리즘이다
  • 할당됐지만, 아직 회수되지 않은 객체를 가리키는 포인터를 포함한 할당 리스트를 사용한다
  1. 할당 리스트를 순회하며 마크비트를 지운다
  2. GC 루트부터 살아있는 객체를 찾는다
  3. 찾은 객체마다 마크비트를 세팅한다
  4. 마크비트가 세팅되지 않은 객체를 찾는다
    4-1. 힙에서 메모리를 회수해 프리 리스트에 되돌린다
    4-2. 할당 리스트에서 객체를 삭제한다
  • 살아있는 객체는 대부분 DFS로 찾는다

GC 용어

STW(Stop the world)

  • GC중에는 모든 application thread가 중단된다

동시

  • GC thread는 application thread와 동시 실행될 수 있다
  • 비싼 작업인데다, 실상 100% 동시 실행을 보장하는 알고리즘은 없음

병렬

  • 여러 thread를 동원하여 GC를 수행한다

정확

  • GC 스킴(계획, 전략)은 전체 가비지를 한방에 수집할 수 있도록 힙 상태에 관한 충분한 타입 정보를 지니고 있다

보수

  • 보수적인 스킴은 정보가 없다
  • 리소스를 낭비하는 일이 잦기 때문에 훨씬 비효율적이다

이동

  • 이동 수집기에서 객체는 메모리를 여기저기 오갈 수 있다 (객체 주소가 고정된게 아니다)

압착

  • 객체 쓰기가 가능한 여백의 시작점을 가리키는 포인터가 있다

방출

  • 수집 사이클 마지막에 살아남은 객체는 모두 다른 메모리 영역으로 이동한다

핫스팟 런타임

객체를 런타임에 표현하기

  • 핫스팟은 런타임에 oop 라는 구조체로 자바 객체를 나타낸다
  • oop는 ordinary object pointer의 줄임말
  • 객체의 위치 정보나 메타정보를 담고있는 단위이다
  • instance가 생성될 때 마다, instance 정보가 담긴 oop가 생성된다

instanceOop

  • instanceOop는 자바 클래스의 인스턴스를 나타낸다

instanceOop의 메모리 레이아웃

  • 객체에 대한 메타정보를 저장한다
  • header + 실제데이터로 구성되어 있다
  • header는 Mark 워드 + Klass 워드 로 구성된다(둘다 기계어 워드)

klassOop

  • Class에 대한 메타정보를 저장한다

instanceOop와 klassOop


(출처: Java 객체는 어떻게 이루어져 있을까?)

  • 왼쪽은 객체의 정보를 담은 instance oop, 오른쪽은 class 정보를 담은 klass oop이다
  • instance oop는 객체 정보(Klass word) + Mark word + 데이터 로 구성되어있다
  • klass oop는 실제 클래스가 가지고 있는 메서드 등의 정보가 담겨있다

GC 루트 및 아레나

GC 루트의 종류
스택 프레임, JNI, 레지스터, 코드 루트, 전역 객체, 로드된 클래스의 메타데이터

  • heap에 있는 객체를 가리키는 참조형 지역변수도 말하자면 가장 단순한 형태의 GC루트이다

  • 핫스팟 GC는 아레나(무대)라는 메모리 영역에서 작동한다

  • 핫스팟은 java heap을 관리할 때 시스템 콜을 하지 않는다 (중요 🌟)

    • Java heap을 영역에 대한 메모리를 할당하고 관리할 때, 운영체제 수준의 시스템 콜을 최소화한다는 의미
    • 핫스팟 JVM은 초기에 heap을 영역에 대한 큰 덩어리의 메모리를 운영체제로부터 할당받는다. 이후 Java 프로그램이 실행되면서 객체를 생성할 때, 핫스팟 JVM은 이미 할당받은 heap을 메모리 내에서 필요한 공간을 할당하고 관리한다. 이 방식으로, JVM은 Java 애플리케이션 실행 중에 객체를 생성하고 삭제하는 과정에서 발생하는 메모리 할당 요청을 처리할 때 매번 시스템 콜을 수행하지 않고, 미리 할당받은 메모리 영역 내에서 작업을 처리할 수 있다. (by. chatgpt)

➡️ 핫스팟은 유저 공간 코드에서 heap을 크기를 관리하므로 단순 측정값을 이용해 GC 서브시스템이 어떤 성능 문제를 일으키고 있는지 파악할 수 있다

할당과 수명

GC가 일어나는 주된 원인?
➡️ 할당률, 객체수명

GC는 "메모리를 회수해 재사용"하는 일이다. 객체는 대부분 단명하므로 GC의 핵심 전제는 동일한 물리 메모리 조각을 몇번이고 계속 다시 쓸 수 있는가 하는 점이다

할당률

  • 일정기간 새로 생성된 객체가 사용한 메모리량
  • JVM이 직접 기록하진 않지만, 비교적 쉽게 측정가능한 값이다 (센섬같은 툴을 쓰면 된다)

객체수명

측정하기 어렵다 ➡️ 핵심요인이다🌟

가설

객체의 수명과 가비지 컬렉션(Garbage Collection, GC)의 효율성에 관한 가설

약한 세대별 가설

  • 시스템의 런타임 작용을 관찰
  • 거의 대부분의 객체는 아주 짧은 시간만 살아있지만, 나머지 객체는 기대수명이 훨씬 길다
    ➡️ 단명 객체를 쉽고 빠르게 수집할 수 있게 설계해야 하고, 장수 객체와 단명 객체를 완전히 떼어놓는게 가장 좋다

🌟객체를 세대에 따라 분류하고, 각 세대별로 다른 방식으로 메모리를 관리한다🌟

  1. 객체마다 세대 카운트를 센다
  • 세대 카운트란, 객체가 지금까지 무사 통과한 GC 횟수이다
  1. 큰 객체를 제외한 나머지만 에덴 공간에 생성한다. 여기서 살아남은 객체는 다른 곳으로 옮긴다
  • 젊은 세대(Young Generation)의 일부
  • GC 발생 시, 에덴 공간에서 살아남은 객체들은 "서바이버(Survivor) 공간"으로 옮겨진다
  1. 장수했다고 할 정도로 오래 살아남은 객체들은 별도의 메모리영역(올드 or 테뉴어드)에 보관한다
  • 장수 했다는 것은, 여러번의 GC 수행에도 살아남은 것이다
  • 이곳에 옮겨진 객체들은 GC가 덜 자주 발생한다. 이는 메모리 관리의 효율성을 높이기 위한 조치이다

에덴 공간

  • 에덴은 대부분의 객체가 탄생하는 장소이고, 단명객체는 다른 곳에 위치할 수 없으므로 특별히 관리를 잘해야 한다

할당

GC 프로세스는 메모리가 부족할 때(heap 메모리 공간이 꽉 채워져 더이상 객체를 생성할 공간이 없을 때) 그때그때 필요에 의해 발생한다
➡️ 예측 가능하지 않다. 불확정적이다. 규칙성이 없다

  • 할당률(일정기간 새로 생성된 객체가 사용한 메모리량)이 높으면 GC는 더 자주 발생한다
    • 할당률이 높다는 것은 이 영역에 많은 객체가 할당되어 있어 사용 가능한 메모리 공간이 적다는 것을 의미한다
    • 메모리가 가득 차게 되면, 새로운 객체를 위한 공간을 만들기 위해, JVM은 사용되지 않는 객체들을 제거해야 하므로 GC가 자주 발생한다
  • 할당률이 너무 높으면 객체는 테뉴어드로 곧장 승격된다 ➡️ "조기승격" 이라고 한다
    • 할당률이 높으면 에덴 공간과 서바이버 공간에 더 이상 새 객체를 저장할 공간이 없거나 부족하기 때문이다
profile
BackEnd Developer

0개의 댓글