일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- Javascript
- stream api
- sqlserver
- Jenkins
- poi
- 자동배포
- 자바8
- 대용량 업로드
- ORM
- 보안
- 그리드
- DevOps
- rabbitmq
- QueryDSL
- jqGrid
- docker
- JPA
- spring
- Stream
- 스트림
- 엑셀 업로드
- mom
- JQuery
- 자동빌드
- java
- MessageQueue
- ci/cd
- mssql
- apache.poi
- 제이쿼리그리드
- Today
- Total
개발 메모장
[Java] OOM과 GC (Out of Memory Error와 가비지 컬렉터) 본문
#. 자바로 개발하는 초급개발자에겐 당연하고 익숙하지만 막상 설명하기 어렵고 면접을 보면 어딜 가든 물어보는 질문일 것입니다.
#. 자바 애플리케이션을 위한 런타임 환경 제공을 하는 JVM GC가 어떻게 흘러가고 어떻게 처리되는지 확인해 보겠습니다.
#. 개발하다 보면 정말 가끔씩 heap space 가 포함된 오류를 볼 수 있을 것입니다.
(낮은 버전의 이클립스를 사용하면 더 높은 확률로 만났던 것 같습니다.)
- 보통 이러한 경우 eclipse.ini에서 -Xms / -Xmx(최소 / 최대) 힙메모리를 지정하여 넘기곤 했을 것입니다.
- 근본적인 오류의 원인은 말 그대로 Heap Memory 공간이 부족하여 생긴 오류입니다.
- 인위적으로 오류를 발생시키기 위해 아래와 같이 코드를 입력해 봤습니다.
public static void outOfMemory() {
List<Object> list = new ArrayList<>();
while(true) {
list.add(new Object());
}
}
- 작성 후 서비스 실행 시 새로운 객체를 생성해 List에 담다 보니 힙 메모리 공간이 부족해 아래와 같은 오류를 내뱉습니다.
- 무한루프 외에도 int형 배열에 객체 생성 시 가능한 최대 크기로 배열을 할당하려 할 때도 발생할 수 있습니다.
- 대용량 파일 업로드 시에도 발생할 수 있습니다.
#. 이렇게 극단적인 경우는 거의 드물며, 천천히 쌓여가는 힙 메모리를 관리하고 비워주는 역할을 GC : 가비지 컬렉터 라고 합니다.
- 자바에선 다양한 GC 알고리즘을 사용 가능합니다.(직렬, 병렬, CMS, G1 등)
- JVM 설정 및 힙 메모리 크기, 애플리케이션 동작, 런타임 조건 등을 통해 적절한 알고리즘을 선택하게 됩니다.
- GC의 주요 역할
1. 메모리 할당 및 해제
- 힙 메모리에 쌓인 개체에 대한 메모리 할당을 관리하며, 참조되지 않은 개체나 사용되지 않는 객체의 메모리를 할당 해제하므로 다른 객체의 사용에 필요한 메모리 공간을 만들어둡니다.
2. 조정 및 구성
- JVM 설정 및 ini 옵션을 통해 GC의 동작, 힙 크기 등 최적화가 가능하도록 도와주며 이에 따른 메모리 관리를 도와줍니다.
3. 런타임 성능 개선
- GC의 수행 시기 및 방법 등은 런타임 성능에 영향을 미치는데 메모리 정리와 애플리케이션 실행의 효율성 간 균형을 맞추기 위한 관리를 도와줍니다.
- GC의 처리 과정
- GC 실행을 위해선 Stop the world를 가장 먼저 실행합니다.(GC 실행 스레드 외 모든 스레드의 작업을 멈추고 GC 처리 후 다시 시작합니다.)
- GC는 Young 영역과 Old 영역으로 나뉘며 이는 각각 Minor GC, Major GC를 처리합니다.
- 또한 Young 영역은 Eden 영역 1개, Survivor 영역 2개로 구성되어 있습니다.
· 처리 방식
1. 새로운 객체가 Eden 영역에 생성됩니다.
2. Eden 영역에서 GC가 실행되며 그중 걸러지지 않은 객체가 Survivor 0 영역으로 이동하게 됩니다.
3. 이와 같은 동작을 반복하며 Survivor 0 영역이 가득 차게 되면 Survivor 0 영역에서 GC가 실행됩니다.
4. Survivor 0 이 가득 찰 때까지 걸러지지 않은 객체는 Survivor 1 영역으로 이동하게 되며 Survivor 0 영역은 비워집니다.
- 2개의 Survivor 영역 중 1개의 영역은 반드시 빈 상태여야 합니다.
5. 이 동작을 반복하게 되며 특정 횟수만큼 반복되는 동안 걸러지지 않은 객체는 Old 영역으로 이동하게 됩니다.
6. 이를 반복하며 Old 영역이 가득 차게 되어 더 이상 Old 영역으로 이동할 수 없게 되면 Old 영역에서 GC가 실행됩니다.
#. GC의 알고리즘 종류
1. Serial GC(싱글 스레드)
- Young 영역에서는 위 설명 방식과 동일하며 Old 영역에서의 방식이 조금 다릅니다.
- Old 영역에서는 Mark-Sweep-Compact라는 알고리즘을 사용합니다.
- Old 영역에 살아있는 객체를 식별하고(Mark) 힙의 앞부분부터 다시 확인해 걸러지지 않은 것만 남깁니다.(Sweep)
- 그 후 객체들을 힙의 앞부분부터 채워 객체가 있는 부분, 없는 부분으로 나눕니다.(Compact)
2. Parallel GC(멀티 스레드)
- 기본적인 알고리즘은 같으나 스레드의 개수가 여러 개인 차이점이 있습니다.
- 따라서 Serial GC보다 속도가 빠르며, 이 알고리즘은 코어 개수가 많을 때 유리합니다.
3. Parallel Old GC
- Serial GC에서 Sweep 단계 대신 Summary 단계를 사용합니다.
- Summary 단계는 앞서 GC를 수행한 영역에 대해 별도로 걸러지지 않은 객체를 식별합니다.
4. Concurrent Mark & Sweep GC
- Initial Mark 단계에서는 걸러지지 않은 객체를 찾습니다.
- 찾은 객체에서 참조하는 객체를 멀티 스레드를 이용해 동시에 Concurrent Mark 단계를 수행합니다.
- Stop the world 및 Remark를 실행합니다. (Stop the World 실행 시간이 짧습니다.)
- 멀티스레드를 동시에 실행하다 보니 자원 사용량(메모리 및 CPU)이 많습니다.
5. G1 GC
- 바둑판의 각 영역에 객체를 할당하고 GC를 실행하는 방식입니다.
- Young 영역, Old 영역에 대한 개념이 아닌 객체를 바둑판에 할당하고 그 바둑판이 꽉 차면 다른 칸에 할당해 GC를 실행하는 개념입니다.
===========================================================
틀린 내용이 있거나 이견 있으시면 언제든 가감 없이 말씀 부탁드립니다!
참고 및 출처 :
https://d2.naver.com/helloworld/1329 /
===========================================================
'Java' 카테고리의 다른 글
[Java] Stream API(1) - 스트림 데이터 생성 (0) | 2023.12.12 |
---|---|
[Java] Zip 파일 생성 후 파일 다운로드(Ajax 이용) (1) | 2023.12.08 |
[Java] 공공데이터 - 공휴일 API 사용하기 (1) | 2023.12.06 |
[Java] String, StringBuffer, StringBuilder의 특징 및 차이점 (1) | 2023.11.29 |
[Java] try with Resources 문(리소스 자동 반환) (2) | 2023.11.27 |