Elasticsearch에서 힙 크기를 32GB 이하로 유지해야 하는 이유

김민규·2023년 2월 26일
0

elasticsearch

목록 보기
2/7
post-thumbnail

Elasticsearch는 메모리를 많이 사용하는 애플리케이션이다.

일반적으로 힙 메모리가 많을수록 그에 비례해서 성능도 올라간다. 🚀

  • 너무 작은 힙 크기는 OOM 오류(Out of Memory Exception)를 발생시킬 수 있다.
  • 너무 큰 힙 크기는 FullGC가 발생할 때 STW(Stop the world) 시간이 길수도 있다.

Elasticsearch에서 힙 크기를 얼마로 설정하면 좋을지 알아보자.

운영체제에 50%의 메모리 공간을 보장하자

Elasticsearch Shard는 내부에 루씬(Lucene)을 가지고 있다.

  • 루씬은 세그먼트 생성 및 관리를 위해 커널 시스템 캐시를 최대한 많이 활용하고 있다.

실시간 검색을 지원하기 위해서는 루씬이 최대한 많은 시스템 캐시를 확보하도록 지원해야 한다.

시스템 캐시

  • 운영체제가 가지고 있는 메모리 공간
  • 커널 내부에 존재

따라서 물리적인 메모리 공간의 50% 정도는 OS가 자유롭게 사용하도록 할당해야 한다.

같이 보면 좋아요. Elasticsearch 캐싱 심층 분석: 한 번에 하나의 캐시로 쿼리 속도 향상

자바8 기반에서는 힙 크기를 32GB 이상 사용하지 말자

Object Pointer는 객체의 메모리 번지를 표현하는 주솟값이다.
힙에 생성된 모든 객체는 이러한 주솟값을 이용해 접근하게 된다.

JVM이 기본적으로 32비트 Object Pointer를 사용하고 있기 때문에 32비트 JVM과 64비트 JVM 모두 32비트 주솟값을 가지고 동작한다.

Ordinary Object Pointer(OOP)

자바에서는 모든 객체가 힙 영역에 생성되고 생성된 객체는 모두 포인터(Pointer)를 가지고 있고 이 포인터를 이용해 객체에 접근한다.
JVM은 힙 영역에 생성된 객체에 접근하기 위해 포인터의 주소를 Ordinary Object Pointer(OOP)라고 하는 특수한 자료구조로 만들어서 관리하고 있다.

32비트 시스템

  • 2322^{32} 까지 표현: 최대 4GB까지의 주소 공간을 가리킬 수 있다.

64비트 시스템

  • 2642^{64} 까지 표현: 이론상 18EB까지의 주소 공간을 가리킬 수 있다.

64비트 시스템의 경우 메모리상의 주소를 가리키는 포인터 1개를 64비트로 표현하다 보니 많은 메모리 공간의 낭비가 발생한다.

  • 인식 가능한 물리적 메모리의 크기가 늘어났지만 물리적인 공간 활용성은 상대적으로 떨어진다.
  • CPU 내부에는 다양한 캐시(LLc, L1, L2)가 있으며 캐시 적중률을 높이기 위해 주메모리와 캐시 사이에서는 지속적으로 값의 이동이 일어남
    • 이동하는 값들도 64비트이기 때문에 32비트에 비해 상대적으로 더 큰 대역폭을 소모한다.

시스템의 패러다임이 32비트 시스템에서 64비트 시스템으로 변화하면서 많은 물리적인 한계를 극복할 수 있었지만 메모리 공간의 낭비나 연산 속도 저하 등의 단점도 함께 나타났다.
✅ JVM에서도 OOP를 위해 낭비되는 메모리 문제를 해결하기 위해 Compressed OOP라는 새로운 개념의 포인터 관리 기법을 도입했다.

Compressed Ordinary Object Pointer

Compressed OOP는 포인터의 공간 낭비를 줄이고 좀 더 빠른 연산을 위해 포인터를 압축해서 표현하는 일종의 트릭이라고 할 수 있다.

  • 이 트릭의 핵심 원리는 포인터가 객체의 정확한 메모리 주소를 가리키게 하는 것이 아니라 상대적인 오브젝트 오프셋(Object Offset)을 가리키도록 살짝 변경해서 동작시키는 것이다.

    8비트 포인터를 이용해 이 트릭을 사용한다면
    256바이트의 물리적인 주소 공간을 표현하는 것이 아니라
    256개의 객체를 가리킬 수 있게 된다.

자바는 데이터 타입에 따라 객체를 8비트(boolean)부터 64비트(Long)까지의 8의 배수 형태로 힙 메모리에 생성한다.

  • Compressed OOP를 이용해 포인터가 객체를 가리키게 한다면 32비트만을 이용해도 포인터가 최대 32GB까지의 힙 메모리 공간을 가리키는 것이 가능해진다.

32비트 Compressed OOP
2322^{32} 까지의 객체(Object)를 가리킬 수 있다.
객체의 최소 단위는 8비트이기 때문에 (2322^{32}) * 8 까지의 메모리 공간을 가리킬 수 있다. (32GB)

Compressed OOP를 자세히 알고 싶다면 이 글을 읽어보자

Compressed OOP를 사용할 경우 포인터를 표현할 때 예외적으로 32비트 포인터를 사용해 동작한다.
🔥 32비트 포인터를 이용하면서도 64비트가 가지는 메모리 낭비 등의 단점을 우회해서 동작할 수 있는 것이다.

하지만 이러한 트릭은 힙 크기가 32GB를 넘어가면 더는 사용할 수 없다.
JVM은 힙 크기가 32GB를 넘어가는 순간 Compressed OOP를 일반적인 64비트 OOP로 자동으로 변환한다. 😱

👉 그래서 Elasticsearch에서는 힙 크기를 설정할 때 최대 32GB 이하로만 설정하라고 안내하는 것이다.

아래 JVM 옵션으로 Compressed OOP를 사용할 수 있다.

jvm.options

-XX:+UseCompressedOops

최신 JDK에서는 Compressed OOP가 기본 설정으로 동작하기 때문에 힙 크기를 32GB 이하로 설정하기만 하면 된다.


참조

profile
Backend Engineer, Vim User

0개의 댓글