Skip to content

Commit

Permalink
자바 최적화 1, 2, 3 chapter 정리 (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
choihuk authored Jan 10, 2024
1 parent bd4dd49 commit 8837916
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 0 deletions.
72 changes: 72 additions & 0 deletions ch01/최혁.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
marp: true
---

# 1. 성능과 최적화

## 최 혁

---

# 자바 성능 개요

- 자바는 일을 하려고 만든 지극히 실용적인 언어이기에 개발 생산성을 위한 성능 희생이 있다.
- managed subsystem을 통해 개발자가 관리해야 할 부담을 덜어준다.(ex. 메모리 관리)
- 반대급부로, JVM 전반에 걸쳐 등장하는 managed subsystem은 그 존재 자체로 JVM 애플리케이션의 런타임 동작에 복잡도를 유발한다.
- 환경이 복잡해 질수록 시스템을 개별적으로 떼어내 생각하기 어렵기에 자바 성능 측정값에 대한 판단은 조심해야 한다.
- 성능을 측정하는 행위 자체도 오버헤드를 일으키기에 너무 자주 샘플링하거나 매번 결과를 기록하는 것 또한 성능 결과 수치에 영향을 미친다.

---

# 성능 분류

### 처리율

> 시스템이 수행 가능한 작업 비율을 나타낸 지표로 보통 `일정 시간 동안 완료한 작업의 단위 수`로 표시한다.
- ex) 초당 처리 가능한 트랜잭션 수
- 처리율이 실제 성능을 반영하는 의미 있는 지표가 되려면 수치를 얻은 플랫폼에 대한 내용(하드웨어 스펙 등)도 기술해야 한다.

### 지연

> 예를 들어 1초에 100리터를 흘려보내는 `수도관 자체의 길이`이다. 즉, 하나의 트랜잭션을 처리하고 그 결과를 반대편 수도관 끝에서 바라볼 때까지 소요된 시간이다.
---

### 용량

> 시스템이 보유한 작업 병렬성의 총량. 즉 `시스템이 동시 처리 가능한 작업 단위(트랜잭션)` 개수를 말한다.
- 용량은 처리율과 밀접한 관련이 있다.
- 보통 용량은 어떤 처리율 또는 지연 값을 전제로 가능한 처리량으로 표시한다.

### 사용률

> 말 그대로 자원의 사용률이다.
- 사용률은 워크로드에 따라 리소스별로 들쑥날쑥하다.
- 계산 집약적 워크로드(그래픽 처리, 암호화)를 주면 CPU 사용률은 100%에 육박하지만, 메모리 사용률은 얼마 안 나온다.

---

### 효율

> `처리율을 리소스 사용률로 나눈 값`이다.
- 같은 처리율을 더 많은 리소스를 들여야 달성할 수 있다면 효율이 낮다고 할 수 있다.

### 확장성

- 리소스 추가에 따른 처리율의 변화는 시스템/애플리케이션의 확장성을 가늠하는 척도이다.
- 시스템 확장성은 궁극적으로 리소스를 투입한 만큼 처리율이 변경되는 것을 지향한다.
- 현실적으로 클러스터를 2배 늘리면 트랜잭션 처리량도 2배로 늘리기는 어렵다.

---

### 저하

> 시스템 사용률이 높을 때 부하가 증가하면 처리율이 더는 늘어나지 않는, 지연 현상이 발생하는데 이런 현상을 부하 증가에 따른 저하라 한다.
### 측정값 사이의 연관 관계

- 다양한 성능 측정값은 어떤 식으로든 서로 연결되어 있다.
Binary file added ch02/최혁/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
82 changes: 82 additions & 0 deletions ch02/최혁/최혁.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
marp: true
---

# 2. JVM 이야기

## 최 혁

---

# JVM

- JVM은 스택 기반 인터프리터 머신이다.
- 물리적인 CPU 하드웨어인 레지스터는 없지만 일부 결과를 실행 스택에 보관하며, 이 스택의 맨 위에 쌓인 값들을 가져와 계산한다.
- JVM 인터프리터는 평가 스택을 이용해 중간값들을 담아두고 가장 마지막에 실행된 명령어와 독립적으로 프로그램을 구성하는 opcode를 하나씩 순서대로 처리하는 `while문 안의 switch문`이다.

---

# 자바 애플리케이션 실행

1. 부트스트랩 클래스가 자바 런타임 코어 클래스를 로드한다. 부트스트랩 클래스로더는 다른 클래스로더가 나머지 시스템을 로드할 수 있게 최소한의 필수 클래스(java.lang.Object, Class 등)만 로드한다.
2. 확장 클래스로더가 생성되고 클래스로더가 차례대로 작동한다.
3. 애플리케이션 클래스로더가 생성되고 클래스패스에 위치한 유저 클래스를 로드한다.

---

### 자바 컴파일러

- 자바 소스 코드를 자바 컴파일러(javac)를 이용해 컴파일한다.(전체 빌드 프로세스의 한 부분)
- 자바 소스코드 -> .class 파일로 변환

### 바이트코드

- 바이트코드는 특정 컴퓨터 아키텍처에 특정하지 않은 중간 표현형(IR)이다.
- 컴퓨터 아키텍처의 지배를 받지 않으므로 이식성이 좋다.
- JVM은 클래스를 로드할 때 올바른 형식을 준수하고 있는지 검사한다.

---

## opcode

![Alt text](image.png)

---

### 핫스팟

- 썬 마이크로시스템즈는 핫스팟 가상 머신을 만들어 C/C++ 같은 언어에 필적할 만한 성능을 끌어냈다.
- 핫스팟은 프로그램의 런타임 동작을 분석하고 성능에 가장 유리한 방향으로 영리한 최적화를 적용하는 가상 머신이다.

### JIT 컴파일

- 자바 프로그램은 바이트코드 인터프리터가 가상화한 스택 머신에서 명령어를 실행하며 시작된다.
- CPU를 추상화했기에 다른 플랫폼에서도 구동 가능하지만, 프로그램이 성능을 최대로 내려면 네이티브 기능을 활용해 CPU에서 직접 프로그램을 실행시켜야 한다.
- 이를 위해 핫스팟은 프로그램 단위를 인터프리티드 바이트코드에서 네이티브 코드로 컴파일한다. -> `JIT(Just-in-Time) 컴파일`

---

- 컴파일러가 해석 단계에서 수집한 추적 정보를 근거로 (핫스팟이) 최적화를 결정하는게 가장 큰 장점이다.
- 최신 성능 최적화를 보려면 핫스팟 새 버전에서 자바 애플리케이션을 실행하는 것이 좋다.

### JVM 메모리 관리

- C,C++등의 언어는 메모리 할당/해제 작업을 개발자가 직접 수행해야 하기에 메모리를 정확하게 계산해서 처리해야 하는 부담감이 있었다.
- 자바는 Garbage Collection이라는 프로세스를 이용해 힙 메모리를 자동 관리하는 방식으로 해결했다.
- Garbage Collection: JVM이 더 많은 메모리를 할당해야 할 때 불필요한 메모리를 회수하거나 재사용하는 프로세스

---

### 스레딩과 자바 메모리 모델(JMM)

- 자바는 1.0부터 멀티스레드 프로그래밍을 기본 지원했기에 개발자가 언제든 실행 스레드를 새로 만들 수 있다.
- 자바 환경도 JVM처럼 멀티스레드 기반이기에 성능 분석이 훨씬 힘들다.

### JVM 구현체 종류

OpenJDK, Oracle Java(라이선스를 제외하고 OpenJDK간 아무런 차이도 없다), Zulu, IcedTea, Zing, J9, Avian, Android

### JVM 모니터링과 툴링

- JMX: JVM과 그 위에서 동작하는 애플리케이션을 제어하고 모니터링하는 범용 툴
- 자바 에이전트: 자바 언어로 작성된 툴 컴포넌트로 java.lang.instrument 인터페이스로 메서드 바이트코드를 조작한다.
Binary file added ch03/최혁/image-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ch03/최혁/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
115 changes: 115 additions & 0 deletions ch03/최혁/최혁.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
---
marp: true
---

# 3. 하드웨어와 운영체제

## 최 혁

---

> 컴퓨터 성능 향상은 소프트웨어의 복잡도 향상으로 이어졌고, 그 결과 성능 향상을 꾀하려면 복잡한 기술에 의지해야 할 수밖에 없게 됐다.
---

# 메모리

- 무어의 법칙에 따라 급증한 트랜지스터는 클록 속도를 높이는데 쓰였고, 이에 프로세스 속도가 매우 빨라졌다.
- 하지만 시간이 갈수록 프로세서 코어의 데이터 수요를 메인 메모리가 맞추기 어려워졌다.(클록 속도와 액세스 속도의 차이 때문에 PU가 논다)
- 이에 CPU 캐시가 고안되었다. 자주 액세스하는 메모리 위치는 CPU가 CPU 캐시에 보관하는 아이디어이다.
- 메모리에 있는 데이터를 캐시로 가져오거나 캐시한 데이터를 메모리에 다시 쓰는 작업은 캐시 일관성 프로토콜이라는 방법으로 해결한다.

---

# 최신 프로세서 특성

- `변환 색인 버퍼(TLB)`는 여러 캐시에 쓰이는 장치로 가상 메모리 주소를 물리 메모리 주소로 매핑하는 페이지 테이블의 캐시 역할을 수행한다.
- `분기 예측`은 프로세서가 조건 분기하는 기준값을 평가하느라 대기하는 현상을 방지한다.
- 따라서 조건문을 평가하는 동안 발생 가능성이 큰 브랜치를 미리 결정하는 휴리스틱을 만들어 미리 추측한 결과를 바탕으로 파이프라인을 채운다.

---

# 운영체제

> 모든 리소스는 한정돼 있고 프로세스는 저마다 리소스를 더 차지하려고 하기에 골고루 나누어줄 중앙 시스템이 있어야 한다.
- 메모리 관리 유닛(MMU)을 통한 `가상 주소 방식``페이지 테이블`은 메모리 액세스 제어의 핵심으로서, 한 프로세스가 소유한 메모리 영역을 다른 프로세스가 함부로 훼손하지 못하게 한다.

### 스케줄러

- 프로세스 스케줄러는 실행 큐(스레드나 프로세스가 CPU 차례를 기다리기 위한 대기 장소)를 이용하여 CPU 액세스를 통제한다.
- 스케줄러는 인터럽트에 응답하고 CPU 코어 액세스를 관리한다.

---

### 컨텍스트 교환

- 컨텍스트 스위치는 OS 스케줄러가 현재 실행중인 스레드/태스크를 없애고 대기중인 다른 스레드/태스크로 대체하는 프로세스이다.
- 유저 스레드 사이에 발생하든, 유저 모드에서 커널 모드로 바뀌면서 일어나든 컨텍스트 교환은 비싼 작업이다.
- 유저 스레드가 preemption 도중 커널 모드로 바꾸어 어떤 기능을 실행해야 할 때 모드가 바뀌면서 다른 캐시를 어쩔 수 없이 강제로 비워야 할 때가 있기에 캐시를 시스템 콜 반환 시 다시 채워야 하여 성능 저하로 이어진다.
- 컨텍스트 교환은 CPU 낭비를 초래한다.
- 리눅스는 가상 동적 공유 객체를 제공하여 이 현상을 방지한다.

---

# 기본 탐지 전략

> 애플리케이션이 잘 돌아간다는 것은 CPU 사용량, 메모리, 네트워크, I/O 대역폭 등 시스템 리소스를 효율적으로 잘 이용하고 있다는 뜻이다. 성능 진단의 첫 단추는 어느 리소스가 한계에 다다랐는지 밝히는 일이다.
### CPU 사용률

- 애플리케이션 성능을 나타내는 핵심 지표이다.
- 유닉스 계열 OS 명령줄에 vmstat, iostate 명령어를 통해 가상 메모리 및 I/O 서브시스템 상태에 관한 유용한 데이터를 신속히 제공해야 한다.

`vmstat 1`: 1초마다 한 번씩 찍어 다음 줄에 결과를 표시한다. 섹션별로 보면 다음과 같다.

---

![Alt text](image.png)

- procs 섹션: 실행 가능한(r), 블로킹된(b) 프로세스 개수
- memory 섹션: 스왑 메모리, 미사용 메모리, 버퍼로 사용 가능한 메모리, 캐시로 사용 가능한 메모리
- swap 섹션: 디스크로 교체되어 들어간 스왑-인 메모리, 디스크에서 교체되어 빠져나온 스왑-아웃 메모리
- io 섹션: 블록-인, 블록-아웃 개수는 각각 블록 장치에서 받은 512바이트 블록, 블록 장치로 보낸 512 바이트 블록 개수이다.
- system 섹션: 인터럽트 및 초당 컨텍스트 교환 횟수
- cpu 섹션: cpu와 직접 연관된 지표를 %로 표기한다.

---

### Garbage Collection

- 가비지 수집을 하려고 커널 교환을 할 일이 거의 없다.
- 따라서 GC 자체는 유저 공간의 CPU 사이클을 소비하되 커널 공간의 사용률에는 영향을 미치지 않는 활동이다.
- 반면 JVM 프로세스가 유저 공간에서 CPU를 100% 가까이 사용하고 있다면 GC를 의심해야 한다.
- GC 로깅은 분석용 데이터의 원천으로서 가치가 높기에 JVM 프로세스는 GC 로그를 꼭 남겨야 한다.

---

### 입출력

- 자바 프로그램은 대부분 단순한 I/O만 처리하기에 I/O 서브시스템을 심하게 가동시키는 애플리케이션 클래스는 적다.
- 보통 I/O를 많이 쓰는 프로세스를 활발하게 모니터링하는 문화가 장착되어 있다.

### 기계 공감

- 기계 공감은 성능을 쥐어짜내는 상황에서 하드웨어를 폭넓게 이해하고 공감할 수 있는 능력이 무엇보다 중요하다는 생각이다.
- 고성능, 저지연이 필수인 분야에서 개발자가 자바/JVM을 효과적으로 활용하려면 JVM이 무엇이고, 하드웨어와는 어떻게 상호작용하는지 이해해야 한다.
- 만약 두 스레드가 동일한 캐시 라인을 수저애서 성능 저하가 발생하면 이런 일이 발생할 수 있음을 이해하고 해결 방법을 찾아야 한다.

---

> `가상화`는 다양한 종류가 있지만 보통 이미 실행중인 다른 OS 위에 OS 사본을 하나의 프로세스로 실행시키는 모양이다.
> ![Alt text](image-1.png)
---

# JVM과 운영체제

- JVM은 자바 코드에 공용 인터페이스를 제공하여 OS에 독립적인 실행 환경을 제공한다. 다만, 스레드 스케줄링같은 기본적인 서비스조차 하부 OS에 반드시 액세스해야 한다.
- 이런 기능은 native 키워드를 붙인 네이티브 메서드로 구현한다. 이 작업을 대행하는 공통 인터페이스를 자바 네이티브 인터페이스(JNI)라 한다.
- java.lang.Object 클래스에는 다음과 같이 non-private 네이티브 메서드가 선언되어 있다.

```java
public final native Class<?> getClass();
public final native void notify();
```

0 comments on commit 8837916

Please sign in to comment.