본문 바로가기
BigData/Spark & Spark Tuning

[Spark Tuning] Spark Memory 와 JVM 정리

by 스파이디웹 2023. 10. 17.
728x90

spark memory를 정리하기 전에 JVM을 알아야 합니다.

왜냐하면 spark는 Java 가상머신(JVM) 기반으로 동작하기 때문이고, 다른 언어(Python,R)로 작성한 spark code도 결국에는 Executor의 JVM에서 실행할 수 있는 Code로 알아서 변환하여 실행되기 때문입니다. 


JVM(Java Virtual Machine)

자바를 실행하기 위한 가상 기계(컴퓨터)

* Java compiler는 JDK를 설치하면 bin 에 존재하는 javac.exe

1. runtime Data Area

Runtime Data Area는 JVM이 프로그램을 수행하기 위해 OS로부터 별도로 할당받은 메모리 공간을 말한다. Runtime Data Area는 크게 5가지 영역으로 나눌 수 있습니다.

1) PC Register

  • JVM의 PC Register는 CPU 내의 기억장치인 레지스터와 다르게 작동
  • PC Register는 각 쓰레드 별로 하나씩 존재하며 현재 수행 중인 JVM Instruction의 주소를 가지게 된다. 즉, 스레드가 어떤 명령을 실행할지 기록하는 부분이라고 할 수 있음

2) JVM Stack

  • 메소드(method)가 호출될 때 메서드와 메서드의 정보는 JVM Stack에 쌓이게 됨
  • 즉, 메서드의 매개변수(parameter), 지역 변수(local variable), return 주소, 임시 변수 등의 정보를 기록하는 스택
  • 각 스레드 별로 생성되기 때문에 다른 스레드는 접근할 수 없으며, 메서드 호출이 종료되면 스택에서 정보들이 제거됨

3) Native Method Stack

  • 자바 외의 언어로 작성된 네이티브 코드들을 위한 스택
  • Java Native Interface를 통해 호출되는 C/C++ 등의 코드를 수행

4) Method Area

  • 모든 쓰레드가 공유하는 메모리 영역으로 클래스, 인터페이스, 메서드, 필드, Static 변수 등의 바이트 코드를 보관
  • Method Area에는 Runtime Constant Pool이라는 별도의 관리 영역도 존재
  • 이는 상수 자료형을 저장하고 참조하여 중복을 막는 역할을 수행

5) Heap

  • Runtime 시점에 동적으로 할당하여 사용하는 영역
  • 클래스를 이용해 인스턴스를 생성하면 Heap에 저장
  • 즉, new연산자를 이용해 생성된 객체를 저장하는 영역
  • Heap은 크게 New/Young 영역, Old 영역, Permanent Generation 3 영역으로 나뉘며, 참고로 java8 이후에는 Permanent 영역이 Metaspace 영역으로 바뀜

Spark Memory 관리

1. 종류

  1. Static Memory Manager (Static Memory Management) 정적
  2. Unified Memory Manager (Unified memory management) 통합

spark 1.6.0부터 통합 메모리 관리자가 Spark의 기본 메모리 관리자로 설정되었습니다.
정적 메모리 관리자는 유연성 부족으로 인해 더 이상 사용되지 않습니다.
두 메모리 관리자 모두에서 Java 힙의 일부는 Spark 애플리케이션 처리를 위해 위치하며 나머지 메모리는 Java 클래스 참조 및 메타데이터 사용을 위해 예약됩니다.

참고:  JVM당 하나의 MemoryManager만 있습니다.

메모리에 대한 정보를 저장하기 위해 두 메모리 관리자는 두 개의 메모리 풀을 사용

  • ExecutionMemoryPool
  • StorageMemoryPool

1-1) Static Memory Manager (Static Memory Management) 정적 메모리 관리

메모리 관리를 위한 전통적인 모델이자 간단한 체계

참고: 정적 메모리 할당 방법은 Spark 3.0에서 제거되었습니다.

장점:

  • Static Memory Manager 메커니즘은 구현하기 쉽습니다.

단점 :

  • 저장 메모리에 여유 공간이 있어도 사용할 수 없으며 실행기 메모리가 가득 차서 디스크 유출이 있습니다. (반대의 경우도 마찬가지)

1-2) Unified Memory Manager (Unified memory management) 통합 메모리 관리

  • 1.6.0 스파크 이상부터, 새로운 메모리 관리자는 스파크를 제공하는 정적 메모리 관리자를 대체 채용 Dynamic 메모리 할당
  • Storage 및 Execution이 공유하는 통합 메모리 컨테이너로 메모리 영역을 할당
  • Execution 메모리를 사용하지 않는 경우 Storage 메모리는 사용 가능한 모든 메모리를 획득할 수 있으며 그 반대의 경우도 마찬가지
  • Storage 또는 Execution 메모리에 더 많은 공간이 필요한 경우, acquireMemory()라는 함수가 메모리 풀 중 하나를 확장하고 다른 메모리 풀을 축소

장점:

  1. Storage 메모리와 Execution 메모리 사이의 경계는 고정되어 있지 않으며 메모리 부족의 경우 경계가 이동 됩니다. 즉, 한 영역이 다른 영역에서 공간을 차용하여 확장
  2. 응용 프로그램에 캐시 및 전파가 없는 경우 불필요한 디스크 오버플로를 방지하기 위해 실행 시 모든 메모리를 사용
  3. 응용 프로그램에 캐시가 있는 경우 데이터 블록이 영향을 받지 않도록 최소 Storage 메모리를 예약
  4. 이 접근 방식은 메모리가 내부적으로 분할되는 방식에 대한 사용자 전문 지식 없이도 다양한 워크로드에 대해 즉시 사용 가능한 합리적인 성능을 제공

2. JVM의 두 가지 유형의 메모리

  1. On-Heap Memory Managment (In-Memory) : Object는 JVM heap에 할당되고 GC에 의해 바인딩
  2. Off-Heap Memory Managment (External-Memory) : Object는 직렬화에 의해 JVM 외부의 메모리에 할당되고 Application에 의해 관리되며 GC에 바인딩되지 않음

일반적으로 Object의 읽기 및 쓰기 속도는 ( On-Heap > Off-Heap  > DISK) 순

 

2-1) 온힙 메모리 (On-Heap Memory)

  • 기본적으로 Spark는 온힙 메모리만 사용합니다. 온힙 메모리의 크기 는 Spark 애플리케이션이 시작될 때 --executor -memory 또는 spark.executor.memory 매개변수에 의해 구성
  • Executor 내에서 실행되는 동시 작업은 JVM의 힙 메모리를 공유
매개변수 설명
spark.memory.fraction (기본값 0.75)-spark1.6이상 실행 및 저장에 사용되는 힙 공간의 비율입니다. 이 값이 낮을수록 유출 및 캐시된 데이터 제거가 더 자주 발생합니다. 이 구성의 목적은 내부 메타데이터, 사용자 데이터 구조 및 희소하고 비정상적으로 큰 레코드의 경우 부정확한 크기 추정을 위한 메모리를 따로 확보하는 것입니다.
spark.memory.storageFraction (기본값 0.5) spark.memory.fraction 에 의해 따로 설정된 공간 내 저장 영역의 크기입니다 . 캐시된 데이터는 총 스토리지가 이 영역을 초과하는 경우에만 제거될 수 있습니다.

Reserved Memory

  1. 예약된 메모리는 시스템용으로 예약된 메모리이며 Spark의 내부 개체를 저장하는 데 사용됩니다.
  2. Spark v1.6.0+부터 값은 300MB입니다. 즉, 300MB의 RAM이 Spark 메모리 영역 크기 계산에 참여하지 않습니다
  3. executor memory < reserved memory *1.5 인경우 에러
21/06/21 03:55:51 ERROR repl.Main: Failed to initialize Spark session.
java.lang.IllegalArgumentException: Executor memory 314572800 must be at least 471859200. Please increase executor memory using the --executor-memory option or spark.executor.memory in Spark configuration.
        at org.apache.spark.memory.UnifiedMemoryManager$.getMaxMemory(UnifiedMemoryManager.scala:225)
        at org.apache.spark.memory.UnifiedMemoryManager$.apply(UnifiedMemoryManager.scala:199)

User Memory

  1. 사용자 메모리는 사용자 정의 데이터 구조, Spark 내부 메타데이터, 사용자가 생성한 모든 UDF, RDD 종속성 정보에 대한 정보 등 RDD 변환 작업에 필요한 데이터를 저장하는 데 사용되는 메모리

Spark Memory

  1. Apache Spark에서 관리하는 메모리 풀입니다. Spark 메모리는 조인과 같은 작업 실행을 수행하거나 브로드캐스트 변수를 저장하는 동안 중간 상태를 저장하는 역할
  2. 모든 캐시/지속 데이터는 이 세그먼트, 특히 이 세그먼트의 스토리지 메모리에 저장
  3. (Java Heap — Reserved Memory) * spark.memory.fraction
  4. Spark 작업은 두 가지 기본 메모리 영역에서 작동
    • Execution – 셔플, 조인, 정렬 및 집계에 사용
    • Storage – 데이터 파티션을 캐시하는 데 사용
    이들 사이의 경계는  spark.memory.storageFraction  매개변수에 의해 설정되며 기본값은 0.5 또는 50%

Storage Memory

  • Storage 메모리는 모든 캐시된 데이터, 브로드캐스트 변수 및 언롤 데이터 등을 저장하는 데 사용됩니다. "unroll"은 본질적으로 직렬화된 데이터를 역직렬화하는 프로세스입니다.
  • MEMORY를 포함하는 모든 지속 옵션에 대해 Spark는 해당 데이터를 이 세그먼트에 저장합니다.
  • Spark는 LRU(Least Recent Used) 메커니즘을 기반으로 오래된 캐시된 개체를 제거하여 새 캐시 요청을 위한 공간을 지웁니다.
  • 캐시된 데이터가 Storage에서 나오면 디스크에 기록되거나 구성에 따라 다시 계산됩니다. 브로드캐스트 변수는 MEMORY_AND_DISK 영구 수준으로 캐시에 저장됩니다. 여기에 캐시된 데이터와 수명이 긴 데이터가 저장됩니다.
  • (Java Heap — Reserved Memory) * spark.memory.fraction * spark.memory.storageFraction

Execution Memory

  • Execution 메모리는 Spark 작업을 실행하는 동안 필요한 개체를 저장하는 데 사용됩니다.
  • 예를 들어, 메모리의 Map 측에 셔플 중간 버퍼를 저장하는 데 사용됩니다. 또한 해시 집계 단계를 위한 해시 테이블을 저장하는 데 사용됩니다.
  • 이 풀은 또한 사용 가능한 메모리가 충분하지 않은 경우 디스크 유출을 지원하지만 이 풀의 블록은 다른 스레드(작업)에 의해 강제로 제거될 수 없습니다.
  • Execution 메모리는 Storage보다 수명이 짧은 경향이 있습니다. 각 작업 후 즉시 제거되어 다음 작업을 위한 공간을 만듭니다.
  • (Java Heap — Reserved Memory) * spark.memory.fraction * (1.0 - spark.memory.storageFraction)
Spark 1.6+에서는 Execution 메모리와 Storage 메모리 사이에 엄격한 경계가 없습니다.
Execution 메모리의 특성으로 인해 이 풀에서 블록을 강제로 제거할 수 없습니다. 그렇지 않으면 참조하는 블록을 찾을 수 없기 때문에 실행이 중단됩니다.
그러나 Storage 메모리의 경우 필요에 따라 블록을 메모리에서 제거하고 디스크에 쓰거나 다시 계산할 수 있습니다(지속성 수준이 MEMORY_ONLY인 경우)

Execution 및 Storage 풀 차용 규칙:

  1. Storage 메모리는 블록이 실행 메모리에서 사용되지 않는 경우에만 Execution 메모리에서 공간을 빌릴 수 있습니다.
  2. Execution 메모리는 블록이 스토리지 메모리에서 사용되지 않는 경우 Storage 메모리에서 공간을 빌릴 수도 있습니다.
  3. Execution 메모리의 블록이 Storage 메모리에서 사용되고 실행에 더 많은 메모리가 필요한 경우 Storage 메모리가 차지하는 초과 블록을 강제로 축출할 수 있습니다.
  4. Storage 메모리의 블록이 Execution 메모리에서 사용되고 스토리지가 더 많은 메모리를 필요로 하는 경우 Execution 메모리가 차지하는 초과 블록을 강제로 축출할 수 없습니다. 그것은 더 적은 메모리 영역을 갖게 될 것입니다. Spark가 Execution 메모리에 저장된 초과 블록을 해제할 때까지 기다렸다가 이를 차지합니다.
    1.  
728x90

댓글