- WAS를 다시보려니 JDK 8도 나오고.... 그러고보니 JDK 5 이후에는 JVM Spec을 찾아본적도 없고.... 간만에 6,7,8 한꺼번에 따라가 봅니다. 내용은 주로 Middleware의 성능튜닝과 관련된 HotSpot 관련 정보입니다.
- 문서에서는 다음과 같은 내용에 대한 정보를 제공합니다.
- JVM 이란?
- JVM에서 Heap과 GC란?
- GC 의 종류는
- GC 적용 전략
- What's new JDK 8
- JVM이란
- JVM의 장점
- 멀티 플랫폼, 높은 호환성
- 대부분의 OS에 맞는 JVM Binary 제공
- 개발 생산성, 잘 다듬어진 JAVA Comp
- Component-inheritance, Implement, Over….
- 개발/운영의 편의성
- 메모리 자동관리 (never malloc/free)
- 시스템 자원 자동할당
- JVM의 단점
- 이 죽일 놈의 속도 (JNI필수, 개선 JIT/hotspot compiler)
- 시어머니가 둘이다! (JNI Interface, OS Interface)
- 자동화의 단점, 통제가 안 된다
- 메모리 정리(GC)는 JVM 맘대로? System.gc()
- 공유객체(static obj)는 말 그대로 공유 쓰레기통?
- 높은 생산성 대비 막 운영(?), 막 개발(?)의 폐해
- 일단 돌아가게 메모리 많이 줘버려!
- 알아볼 수 없는 코드, 1000개 조회 해서 10개 쓴다, 나쁜 유지관리
- OS 측면에서 보면 JVM도 Process 일 뿐이다.
- JAVA Process는?
- JAVA Process는?
- JVM입장에서 보면 WAS도 Java Application 일 뿐이다.
- JVM위에서 구현된 Application = Web Application Server
- 성능과 자기만의 색깔을 위해 도입된 JNI 와 확장 Package들
- JNI : Java Native Thread 들, JVM의 취약점인 IO관련 작업을 C로 처리한다
- 사용자 요청을 받아들이는 Server Socket 부문
- DB Connection을 생성하는 Client Socket 부문
- Log Write를 관리하는 File IO부문
- 확장 Package
- Open Source 들 중에 괜찮은 Pkg를 선택해서 추가한다.
- 추가할때 그냥 하면 빈티 나니까.... 의존성을 높이기 위해... 편의 API를 제공한다.
- JMS를 쓰겠다고 Weblogic Intigration API를 사용했다면 Weblogic에 의존적이됨
- Session 공유를 하겠다고 JBoss Infinispan을 쓰면 JBoss와 노예계약 (잘 쓰면 득 못쓰면 독)
- JNI : Java Native Thread 들, JVM의 취약점인 IO관련 작업을 C로 처리한다
- WAS의 일반적인 버그는 기능 설정과 관련이 있다
- WAS의 Critical 버그는 JVM 버그에 의존적이다.
- JVM에서 Heap 과 GC란
- Heap
- JAVA Object들의 작업 공간 (Sun(oracle) JVM / HP JVM Architecture, not equal IBM / BEA(ORACLE) jrockit)
- Sun JVM 구조
- NEW(Eden) : 신규 생성 되는 객체 들이 저장 되는곳
- Survivor 1(From,ss1),2(To,ss2) : Old로 이전 되기 전 Promotion 대상이 되기 위해 거쳐 가는 곳
- Old : 오래(?) 사용되어야 하거나 NEW가 부족한 경우 저장되는 공간
- Perm(Permanent) : java code, method들의 List,Static Object 들이 저장되는 공간 (메타데이터라고 들 한다),
- Stack : Thread 당 할당된다.(너무 크게 잡으면 메모리 낭비, 적으면 Stack Overflow)
- Native(Other) : JNI관련 시스템 Object들이 사용하는 공간
- !! Xmx 2G로 잡았는데 Top 명령에서 RSS를 보면 2G를 넘는이유는 Stack 과 Native가 잡는 영역 때문이다.
- !! 예를들어 기본적으로 Xmx보다 너무 크다면 Xss를 너무 크게 잡으면 늘어난다.
- !! 동적으로 Xmx 보다 크게 늘어난다면 File IO 또는 Socket IO 용 버퍼가 크거나 많이 사용되면 늘어날 수 있다.
- !! JIT로 인한 증가는 일반적으로 수백Mbyte 이하이다. JIT로 인한 메모리 누수에는 아래와 같이 JIT대상에서 제외 할 수 있다.
- -Xjit:exclude={package/class.method|package/class.method}
- 또는 파일에 exclude 대상을 리스팅하여 넣고(한줄에 제외할 메서드 하나씩)
- .hotspot_compiler 파일을 만들고 안에다가 내용을 넣는다.
- exclude com/amir/SomeClass someMethod (한줄에 하나씩, Class를 넣어도 되고 Method를 넣어도 된다.)
- -XX:CompileCommandFile=/my/excludefile/location/.hotspot_compiler
- .hotspot_compiler 파일을 만들고 안에다가 내용을 넣는다.
- JNI로 별도로 개발된 모듈의 메모리 누수는 ..... JNI 할아버지를 불러야 한다...
- SUN JVM 구조
- BEA JVM 구조 (Nursery : 보육장 - Sun의 ᅟ Young)
- IBM JVM 구조
- Sun JVM G1 구조
Humongous(엄청난?) : region의 50%가 넘는 크기의 Obj가 생성되면 Humongous region(몇 개의 region을 합함)으로 저장한다. - Sun JVM의 상세 구조
- Heap에 저장되는 Object의 구조
- Garbage Collection
- 이란
- Referrer가 없는 Object 들을 메모리에서 제거(Minor, MajorGC) 하거나 지속해서 사용할 Object를 Promotion 하여 저장(OLD)
- 단편화된 메모리 공간을 조각 모음 하는 작업
- Live,reachable Object : 현재 사용 중인 Object로 GC대상이 아니다.
- Unreachable Object : 사용하지 않는 Objject의경우 GC 대상이 된다.
- 사용중 과 아님의 기준은? : 참조하는 상위클래스가있는가 == 나에게 referrer가 있는가
- Static Object : Static Object로 선언한 객체의 경우 GC 대상이 아니다.
- 위와 같은 그림이지만 Root set이라는 개념과 Obj중 상위 클래스를 참조하는 경우라도 상위 클래스의 참조가 없으면 unreachable 이라는것이 설명 가능해서 추가함
- 메모리 관리를 위한 기본 컨셉
- weak generational hypothesis (영역에 대한 자신 없는 가설)
- 대부분의 객체는 금방 접근 불가능 상태(unreachable)가 된다.
- 오래된 객체에서 젊은 객체로의 참조는 아주 적게 존재한다.
- 그래서 두 개의 영역으로 쪼갰다
- Young 영역(Yong Generation 영역): 새롭게 생성한 객체의 대부분이 여기에 위치한다. 대부분의 객체가 금방 접근 불가능 상태가 되기 때문에 매우 많은 객체가 Young 영역에 생성되었다가 사라진다. 이 영역에서 객체가 사라질때 Minor GC가 발생한다고 말한다.
- Old 영역(Old Generation 영역): 접근 불가능 상태로 되지 않아 Young 영역에서 살아남은 객체가 여기로 복사된다. 대부분 Young 영역보다 크게 할당하며, 크기가 큰 만큼 Young 영역보다 GC는 적게 발생한다. 이 영역에서 객체가 사라질 때 Major GC(혹은 Full GC)가 발생한다고 말한다.
- 그래도 느리다.
- 병렬로 처리 하자
- 실시간으로 처리하자
- 구조적으로 도저히 방법이 없다. 해결하자
- New/Old개념은 가져가되 구조적 영역은 포기
- CG First(G1) 알고리즘 제공
- weak generational hypothesis (영역에 대한 자신 없는 가설)
- 하면
- 기본적으로 JVM은 STW (Stop The World) 된다. (GC 튜닝은 STW을 줄이는것이 최종 목표)
- 하고 나면
- 필요한 메모리가 정리되고 새로운 Object 생성이 가능하다
- 실패하면
- 실패하는 이유
- 생성해야 할 Object가 너무 큰 거나 Heap이 부족하거나
- 생성하고자 하는 Object가 New 영역보다 작을때 : Minor GC를 수행하고 남는 공간이 생성할 공간만 큼 되면 생성한다.
- 생성하고자 하는 Object가 New 영역보다 크거나, Minor GC후에도 공간이 없으면 : Old 영역으로 바로 Promotion을 진행한다.
- 생성하고자 하는 Object가 Old 영역보다 작고, Old 영역에 여유가 있다면 : 정상적으로 생성하고 진행한다.
- 생성하고자 하는 Object가 Old 영역보다 작고, Old 영역에 여유가 없다면 : Full GC를 수행하고 저장을 시도한다.
- Full GC후에도 Old 영역에 여유가 없다면 : OOM 발생하고 객체 생성에 실패한다.
- 생성하고자 하는 Object가 Old 영역보다 크면 : OOM 발생하고 객체 생성에 실패한다.
- 주의사항
- !! OOM이 발생해도 이론상 JVM이 깨지거나 문제가 발생해서는 않된다.(해당 APP의 Exception발생은 정상적인것임)
- !! OOM이 발생하면서 JVM에 무한 Full GC가 나거나 Hang 상태로 보이는 이유는 우리가 본 시점의 OOM이 문제가 아니라 OOM이 발생되도록 메모리를 점유하고 있는 그 무엇인가가 문제이다. (로그를 보고 OOM발생 Exception의 APP를 무조건 의심하면 안된다.)
- Object가 지속적으로 Heap을 점유하는 경우
- 생성해야 할 Obj의 크기가 크지 않더라도 절대적인 Heap의 여유 공간이 없다면 OOM이 지속적으로 발생한다.
- 대표적인 OOM 발생 원인은
- Static 객체에 연결한 대량의 정보
- 코드상에 Static final로 정의한 사용자 Obj
- 세션 객체에 저장한 대용량 Obj
- WAS 연계 시스템의 지연
- DB Query가 무겁거나 DB에 문제가 있어 응답이 느리면 해당 처리를 위해 Thread가 대기하고 Thread가 사용중이던 Heap의 Object들은 Refere가 유지되므로 OOM 발생이 될 수 있다.
- 기타 원격지 연계시스템(결제, 승인, 실명확인 등)의 경우에도 동일한 증상으로 인하여 OOM 발생위험이 있다.
- 마구잡이 개발
- DB Cache 객체를 사용하는데 적절한 통제 없이 마구 저장할 경우 절대적인 heap 사이즈가 부족해진다.
- 잘못된 코드로 대량 객체를 생성하고 해당 객체를 JSP등에서 세션 범위에서 사용하는 경우 사용자가 늘어나면 OOM이 발생한다.
- Static 객체에 연결한 대량의 정보
- 생성해야 할 Object가 너무 큰 거나 Heap이 부족하거나
- Promotion 이란
- New 영역은 사용자 호출에 의해 처음으로 생성된 객체(Object = 실 메모리를 점유하는)가 만들어지는 1차 작업 공간입니다.
- 이 공간의 영역은 제한적(-Xmn)이므로 지속적으로 사용될 것 같은 Object들은 절차에 따라 Old 영역으로 넘어가게 되며 이 절차를 Promotion이라고 합니다.
- GC에서 사용되는 단어 설명
- Mark : Object가 현재 사용되고 있는지에 대한 검사, 기준은 Referrer가 있는가, 즉 이 Object가 Applicaiton Code상에서 연결된 것이 아직 사용되고 있는가 (Minor GC)
- Sweep : Mark 대상이 된 Object를 불용처리합니다, 실제 메모리를 지우는 작업은 아니고 File 삭제시 FAT에서 정보만 지우듯이 Object 메모리 주소번지만 제거됩니다. (Minor GC)
- Compact
- 조각모음이라고 보시면 됩니다. 이 작업은 평소에는 수행 되지 않으며 Full GC 시에 수행됩니다. 이 작업의 기본컨셉은 STW(Stop The World)
- sweep 된 Object들이 차지하던 공간을 다른 Object들을 이동시켜 메꾸어 단편화를 정리합니다.
- JVM의 Heap 공간의 메모리 관리는 Object가 생성 될때 해당 Object가 차지 하는 공간이 연속된 주소공간으로 확보되어야 합니다.
- 100M의 여유공간이 있더라도 단편화 되어 연속된 빈 공간이 없다면 Compact를 통하여 정리하게 됩니다. (Major GC)
- Promotion의 진행 순서는 아래와 같이 진행됩니다. (GC 알고리즘에 따라 조금씩 다르지만 기본 사항으로 이해 하시면 됩니다. G1은 완전 다름...)
- 마이너 GC가 발생 되는 순간의 그림입니다.
- Yonung 영역에서 Referer가 없는 object는 제거 되고 사용 중인 경우 Survivor space로 이동 됩니다. (From,To의 이동은 통째로 이동 됩니다. 그래서 두 개의 영역의 사이즈는 동일하고, survivor로 들어가는 Obj도 이번 차수에 이동될 영역으로 들어갑니다.)
- From 영역에서 Referer가 없는 객체가 제거 됩니다.
- From, to를 수 회 왔다 갔다(-XX:MaxTenuringThreshold=32) 한 Obj가 아직 referer가 살아 있다면 Old로 넘어갑니다.
- From에 저장되어 있던 obj 중에서 아직 Old로 넘어갈 카운트가 안된 경우 To로 이동 됩니다. (MaxTenuringThreshold 수치까지 왔다리 갔다리 합니다.)
- Old에는 앞서 단계를 거치고 살아남은 Obj가 저장되어 있습니다.
- 마이너 GC가 수행 되고 나면 다음과 같이 정리 된 상태가 됩니다.
- 아래는 Compaction(Full GC)의 그림입니다.
- Full GC & GC Type
- 란?
- Old 영역 부족, Perm 영역 부족에 의하여 발생
- JVM이 상대적으로 느린 것은 어쩔 수 없고, 결국 STW(Stop The World)의 최소화가 관건이다.
- 가 일어나면?
- 처리 되는 기간 동안 JVM은 동작을 멈춘다
- 일반적인 기준으로 JVM의 가용율에서 STW는 5% 미만으로 통제하여야 한다.
- 24시간 운영시 ( 86400초 * 0.05 = 4320초 ) 최대 72분 내로 통제 (웹서비스 에서는 이것도 너무 러프 하다)
- 다만 연속적이면 무조건 안 된다. (말도 안되기는 하지만 72분 동안 WAS가 정지하면 누가 이해 하겠어?)
- 간격으로 이야기 하자면 허용 Duratiuon Time이 5Sec 라면 되도록 균등해야 하므로
- 하루의 5%를 GC허용 시간으로 가정하면 = 72분
- GC허용 시간을 GC수행 허용 시간(duration) 나누면 = 864회(72분 / 허용시간 = 4320sec / 5sec) 이하 발생해야 하므로
- 1일 기준 가용시간 = 86400sec - 4320sec = 859680sec ( = 1일 - 허용GC시간)
- 허용 발생회수를 나누면 = 859680sec / 864회 = 995초(약 17분) 간격을 평균 간격으로 이야기 할 수 있다.
- 실패하면
- VM입장에서는 메모리를 정리해야 다음 처리가 가능하므로 무한 Full GC가 반복된다. =지속적 STW
- APP 입장에서는 Out Of Memory Exception이 발생하고 처리가 중지된다.
- 간혹 JVM이 죽어버리는 경우도 발생한다.
- 결과적으로 Full GC가 발생하는 것은 인정하지만 실패는 발생하면 안된다.
- CMS에서의 Full GC(CMS는 Full GC를 예방하는 방식이지만 한번 발생하면 대책이 없다...)
- CMS는 기본 구조에서 Old 영역에 대한 Compaction 작업을 수행하지 않습니다. (메모리 단편화가 증가함)
- JAVA의 Heap 구조에서 Obj는 연속된 메모리 주소공간이 확보 되지 않으면 객체 생성에 실패합니다. (단편화 되어 100M가 있어봐야 Obj는 생성에 실패할 수 있다)
- 이러한 이유로 Old 영역이 실 사용량 보다 적거나 너무 타이트하게 할당될 경우 CMS 무한 Full GC가 발생할 수 있습니다.
- 해결하기 위한 컨셉은 아래와 같은 옵션으로 접근한다.
- -XX:SurvivorRatio : From, To 영역의 사이즈를 키워서 작은 obj(단편화 원인)를 Old로 Promotion 되지 않도록 유도(From, To 에서 GC 되기를 기대할 뿐 늘린다고 해결 되지 않는다)
- -XX:MaxTenuringThreshold : Promotion 조건인 From, To 영역의 이동 카운트값을 늘려서 더 많이 이동(왔다리 갔다리)하게 만들어서 Old로 Promotion 되지 않도록 유도
- -XX:CMSInitiatingOccupancyFraction : CMS 동작의 트리거를 좀 더 빨리 동작하도록 한다, 예를 들어 사용율 60%부터 동작한다면 30%로 줄여서 미리 CMS를 구동해서 단편화를 제거한다(compact를 하는것이 아니기때문에)
- -XX:+UseCMSInitiatingOccupancyOnly :항상 CMS를 점진적으로 사용하도록 합니다.
- STW 개선방안은? (GC 알고리즘)
- JVM의 GC에서 메모리를 정리하는 것은 JVM 내에 있는 APP(HotSpot)이다. 이 APP는 Thread로 동작한다.
- Serial GC(-XX:+UseSerialGC) : 기본적은 GC는 1개의 Core가 있는 시스템등에 적용하기 위한 GC로 PC Application등에는 가능하나 서버에는 적용해서는 안된다.
- Parallel GC(-XX:+UseParallelGC) : JDK6에서는 기본 GC, N 개의 GC Thread가 있으면 전체 STW Time은 1/n 이다 (이론적으로 하지만 좀 더 걸린다.)
- Parallel Old GC(-XX+UseParallelOldGC) : JDK5 update 6부터 제공되었으며 Parallel GC와 동일하지만 Old 영역 GC 구조가 변경되어 Mark-Summary-Compaction 단계를 거친다. (
XX:+UseParallelGC를 적용하면 기본 활성화 됨)
- Concurrent Mark&Sweep GC(-XX:+UseConcMarkSweepGC) : N개의 Thread + 계속적인 GC를 통하여 STW이 발생하지 않도록 최소화 하는 방법도 있다. (CMS라고 부른다)
- Minor GC발생시 Old도 덤으로 GC하는 Train GC(-Xincgc) = Incremental GC 도 있다. (컨셉은 훌륭한데....거의 사용 안함), JDK8에서 Deprecate 됨
- G1 (Garbage First)GC(-XX:+UseG1GC) : JDK 6에서 early access로 제공했으며 JDK 7 update4 에서 정식으로 적용되었다. Server style GC라고 부른단다 ^^
http://www.oracle.com/technetwork/java/javase/tech/g1-intro-jsp-135488.html- CMS가 Old 영역의 Fragement에 대응하지 못하고 무한 Full GC를 수행하는 문제점이 있고 이를 해결 하기 위한 방안으로 제시되었다
- Multi Process + Lage Memory 시스템을 대상으로 한다. (Heap 1~2G 시스템에 적용하면 Overhead로 오히려 성능 문제가 발생할 수 도 있을 듯....이건 추정임)
- New,Old 영역에 연속된 메모리 주소(물리적) 에 대한 구조적 개념을 버리고 메모리 영역을 통으로 관리 함으로써 프로모션이라는 것 자체가 없어졌다.
- 전체 메모리를 Region(지방,부문) 이라고 부르는 대략 1Mbyte단위(-XX:G1HeapRegionSize=size)의 블럭형식 단위로 분할 하고 이 영역에 객체를 생성한다.
- JDK 8 기본 사이즈는 JVM이 상황 봐서 정의 한답니다. The default region size is determined ergonomically based on the heap size.
- JDK 6 u26에서는 init heap size / 2048 이였다. (아마도 JDK8도 동일할 듯)
- New,Old 가 없으므로 Promotion이 없으며 메모리 할당 제어를 Evacuation(이바큐에이션-소개,비우다) 이라고 부른다. (개념은 비슷함)
- 내부적으로 GC동작에 대한 기준은 STW 시간에 대한 목표치를 가지고 동작한다. –XX:MaxGCPauseMillis=<n>, default value = 200ms. (거의 실시간으로 봐야할 듯)
- 목표치 시간에 대한 GC가 될 수 있도록 내부적으로 추정해서 동작한다.(알고리즘은 잘 모르겠어요....) 즉 짧게 지정할 수 록 GC가 빨리 동작하고 = 자원을 많이 먹는다
- 기존 Old 영역에서 참조하는 New 영역의 객체 정보(referrer)를 관리하던 Card Table이 전체 region에 대한 객체 referrer에 대한 관리형태로 변경되었다.(Remembered Sets 이라고 한다)
- 2014년 Q1 현재 G1을 공식적으로 지원하는 JVM은 ORACLE JDK(Sun JDK) 뿐이다.
- 현재 G1 GC로그를 분석할 수 있는 툴은 개발되어 있지 않다. log를 보고 직접 분석해야 한다. (-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC)
- -XX:+UnlockExperimentalVMOptions 옵션은 JVM에 실험적 옵션을 활성화 하는 옵션이다.
- JDK7 update4 부터는 위 옵션을 넣지 않아도 된다. (jdk6, jdk7 u3이하에 사용)
-
Option and Default ValueDescription
-XX:+UseG1GC Use the Garbage First (G1) Collector -XX:MaxGCPauseMillis=n Sets a target for the maximum GC pause time. This is a soft goal, and the JVM will make its best effort to achieve it. -XX:InitiatingHeapOccupancyPercent=n Percentage of the (entire) heap occupancy to start a concurrent GC cycle. It is used by GCs that trigger a concurrent GC cycle based on the occupancy of the entire heap, not just one of the generations (e.g., G1). A value of 0 denotes 'do constant GC cycles'. The default value is 45. -XX:NewRatio=n Ratio of old/new generation sizes. The default value is 2. -XX:SurvivorRatio=n Ratio of eden/survivor space size. The default value is 8. -XX:MaxTenuringThreshold=n Maximum value for tenuring threshold. The default value is 15. -XX:ParallelGCThreads=n Sets the number of threads used during parallel phases of the garbage collectors. The default value varies with the platform on which the JVM is running. -XX:ConcGCThreads=n Number of threads concurrent garbage collectors will use. The default value varies with the platform on which the JVM is running. -XX:G1ReservePercent=n Sets the amount of heap that is reserved as a false ceiling to reduce the possibility of promotion failure. The default value is 10. -XX:G1HeapRegionSize=n With G1 the Java heap is subdivided into uniformly sized regions. This sets the size of the individual sub-divisions. The default value of this parameter is determined ergonomically based upon heap size. The minimum value is 1Mb and the maximum value is 32Mb. - G1의 동작 (기존과 다르게 G1은 GC가 시작되면 Young region 정리(MinorGC)후 Evacuation 과 Compaction(MajorGC)가 연속으로 진행된다.)
- 기동
- G1으로 설정한 JVM은 New/SS/Old 로 구분되는 물리적 메모리 구분 없이 region으로 불리는 메모리 블럭을 구성하면서 기동 된다.
- 논리적으로 New/SS/Old는 존재 하지만 물리적으로 주소번지로 구분 되지 않는다.
- 초기 Object들은 임의의 region에 생성되며 해당 obj의 referrer 정보는 Remember set(전체 Heap에서 5%수준을 사용)에 저장된다.
- region 크기보다 큰 obj가 생성되어야 한다면 몇 개의 region에 걸쳐서 Object를 생성하게 되고(인접해 있는) 이러한 region 집합을 Humongos라고 부른다. 이 정보도 remember set에 저장된다.
- ᅟYoung GC = Minor GC
- 수행단계 (Marking -> Remarking -> Evacuation )
- Concurrent Mark : Single Thread로 진행, 정지없음, remember set의 referrer 정보를 참조하여 live가 아닌것만 빠르게 마킹한다.
- Remarking : 병렬로 진행, 정지 없음, Region마다 live obj의 밀도(전체 Obj중에서 Live 비율)를 계산하고 만약 UnReachable Obj만 있을 경우 Region 자체를 폐기한다.
- Evacuation : 병렬로 진행, 정지시간 있음. region의 live obj 밀도가 낮은 region에 대하여 Evacuation을 실행한다.
- 정리 대상 region에서 live obj는 새로운 region(Promotion age에 따라 ss 또는 old region) 으로 복사 된다.
- 논리적으로 Serviver 영역(SS)인 다른 region으로 복사 거나 Live Obj만 있는 region(논리적으로 Old영역)으로 복사된다.
- 이동 후에 unReachable obj만 있는 region은 폐기된다.
- 설명
- 시간이 지나면 referrer가 없는 obj들이 증가 되게 된다. (remeber set을 참고하여 지속적으로 region에 대하여 Concurrent Mark가 수행됨)
- Minor GC는 실시간으로 지속적으로 수행 되나 전체에 대한 GC는 아니고 region단위로 수행되며 Live Obj가 없는 region은 폐기함으로써 메모리를 정리하게 된다. (전체적으로는 메모리가 정리되지만 단편화가 증가한다.)
- 수행단계 (Marking -> Remarking -> Evacuation )
- Old GC = Major GC
- 수행단계 (Evacuation -> Compaction)
- Evacuation
- yong GC의 Evacuation과 동일한 방식으로 시작한다.
- 앞서 Scan한 정보를 기반으로 region의 live obj 밀도가 낮은 region에 대하여 Evacuation을 실행한다.
- 정리 대상 region에서 live obj는 새로운 region(Promotion age에 따라 ss 또는 old region) 으로 복사 된다.
- 논리적으로 Serviver 영역(SS)인 다른 region으로 복사 거나 Live Obj만 있는 region(논리적으로 Old영역)으로 복사된다.
- 이동 후에 unReachable obj만 있는 region은 폐기된다.
- 이때 앞서 진행하면서 Survivor region으로 지정된 영역과 Old region으로 지정된 영역이 이동 되어 메모리 앞쪽으로 정리된다.
- yong GC의 Evacuation과 동일한 방식으로 시작한다.
- Compaction
- Evacuation을 거치고 나면 전체 메모리에서 Live Obj가 있는 young region에 해당하는 영역들이 듬성듬성(단편화) 존재하게 된다.(위의 그림 우측)
- 메모리가 부족하거나 GC 수행시간이 설정보다 길어질 것으로 판단되면 GC를 다시 수행하면서 region들을 앞쪽으로 이동 시켜 정리하게 된다.
- Evacuation
- 설명
- Evacuation작업은 Minor(yongGC)와 동일한 작업을 수행하지만 정리된 region을 이동시키는 부분이 조금 다르다.
- G1의 Compaction 작업은 region 단위로 이루어지며
- 해당 region을 참조하는 Thread가 잠시 느려지기는 하지만
- 전체적으로 서비스는 진행 중이므로 STW은 발생하지 않는다. (아주 짧은 정지는 있다)
- 수행단계 (Evacuation -> Compaction)
- 기동
- JVM의 GC에서 메모리를 정리하는 것은 JVM 내에 있는 APP(HotSpot)이다. 이 APP는 Thread로 동작한다.
- Garbge Collection strategy
- Throughput VS Response Time trade-off
- 단위 시간당 일량(Throughput)을 증가시키려면 GC소요시간=횟수를 줄여야 하고 GC를 줄이기 위해 CMS나 병렬GC를 사용하는 경우 CPU자원을 더 사용하게 되고
- 정해진 자원에서 더 많은 CPU를 사용한다는 것은 응답시간의 증가와 같으므로 Response time과 반비례 하게 된다.(라고 문서상에 표현한다.)
- 하지만 최근에는 워낙 장비의 CPU Power들이 좋기 때문에 GC를 줄이기 위해 복잡한 GC 알고리즘을 사용한다고 Response time과 반비례 하지는 않는다.
- GC를 실시간으로 처리하는 경우 비교적(GC를 한꺼번에 하는 것 보다는) Response Time이 증가하지만 성능상 영향은 미미하므로 무시할 수준이라고 판단하면 됩니다.
- 쓰루풋 중심 튜닝 (WEB Service는 요고이 중요하다)
- 응답시간을 조금 늘이더라도 GC가 최대한 발생하지 않도록 한다.
- Parallel Algorithm
- 여러 개 Thread 가 Full GC 를 최대한 빨리 끝낸다.
- CMS에 비하여 안정성 높음
- Concurrent Algorithm (CMS)
- 여러 Thread + 실시간으로 Mark & Sweep = Full GC 최대한 방지
- CPU 자원 가장 많이 사용
- Parallel과 Serial에 비하여 안정성에 문제가 있다. Object Type(small or big size)JVM 및 OS 마다 조금씩 다르다
- G1
- 메모리 영역을 순차적으로 관리하지 않고 1Mbyte 단위의 Region이라는 블럭들을 사용하고 Compact를 배제 시킴으로써
- GC의 발생을 줄이는 것 과 함께 GC 수행시간을 개선시킴
- 메모리상의 region에 대한 연관관계(referrer)를 관리하기 위한 Overhead가 있으므로 작은 heap 영역을 사용하는 경우 오히려 좋지 않을 수 도 있다(Xmx 2G 이하 등)
- 응답성능 중심 튜닝
- Default(Serial) Algorithm
- Full GC가 발생하더라도 속도가 중요하다.
- 단독 APP의 경우 기본이 차라리 나을때가 있다.
- 업무 형태에 따른 튜닝
- 포털 VS 업무시스템
- 포털
- 다수의 사용자가 짧은 방문시간을 유지하며 많은 페이지를 호출한다.
- 이 경우 JVM에서는 Old 보다는 New영역에 대한 객체 생성 관리가 중요하다.
- 최대한 Old영역으로의 Promotion을 적게 하도록 하여 Full GC로 인한 정지시간이 작아지도록 하고
- 작은 Obj들이 old에 저장됨으로써 발생하는 단편화가 작아지도록 적용해야 한다.
- 관련 옵션
- -XX:NewRatio=3 (default 2) : heap에서 New영역에 대한 비율 정의, New 영역을 크게(Old보다는 작아야 한다)하여 더 많은 객체를 저장하도록 하여 Old Promotion을 줄인다.(Ratio 가 아닌 -Xms= -Xmx= -XX:NewSize= -XX:MaxNewSize= -XX:SurvivorRatio= 를 조합해도 된다.)
- -XX:SurvivorRatio=2 (default 8): New영역에서 Survivor 영역에 대한 비율, 기본 사이즈보다 크게 해서 Old Promotion을 줄인다.
- -XX:TargetSurvivorRatio=90 : SS 영역정리 기준을 기존 50%에서 90%로 올려 Old Promotion을 줄인다.
- -XX:MaxTenuringThreshold=64 (default 15/CMS = 4): SS에서 새로 만들어졌고 , 작은 크기의 Obj들이 SS1,SS2를 더 많이 채류하도록 하여 Old Promotion을 줄인다.
- 원장조회, 매출실적 조회
- 정해진 인원이 오랜시간동안 대량의 데이터를 조회
- 일반적으로 비정형화 된 데이터에 대한 조회를 수행하는 경우 New 영역의 사용빈도가 낮다.
- New 영역보다 큰 데이터 사이즈가 빈번하게 저장되므로 주로 Old로 바로 프로모션되는 케이스가 많다.
- 이러한 경우 실시간 계열의 GC는 큰 효용성이 없다.
- Old 영역을 크게 잡아주고 GC는 단순하게 하는것이 좋다(그래도 single 보다는 Para를 적용한다. CMS는 비추천)
- 대량 장표 조회는 WAS에서는 어렵고 최근에는 BI로 거의 넘어갔으며 Bigdata 쪽으로 트렌드를 가지고 간다.
- 포털
- 포털 VS 업무시스템
- CASE Study
- Mass traffic web site Heap Tuning
- JVM Option
- 설명
New Old 비율은 성능에 큰 영향을 줍니다. 일반적으로 비율은 2, 3 에서 이론적 수치가 가장 잘 나옵니다.
다만 실제 Production에서는 다양한 형태의 Object들이 생성되기 때문에 이론적 TEST결과와 부합되지 않으므로 비율 설정에 따른 모니터링 결과를 반영해야 합니다.
- Back Office 에서 Full GC
- 기업의 back office applicaiton이라 함은 ERP나 벌크성 데이터를 수집 가공하는 업무가 많음
- 이런 경우 너무 많은 SQL 결과값에 의하여 Heap 영역이 부족함으로 인한 문제가 종종 발생함
- Full GC가 일어나는 순서는 아래와 같다.
- 너무 많은 SQL 결과값 조회
- New 영역에 저장 되기에도 크다 OLD로 그냥 프로모션 된다.
- 아직 처리가 안 끝났다 = 레퍼런스가 살아 있다. GC대상이 되지 못한다.
- Full GC
- 해결방안은
- Heap 크기를 늘려서는 대부분 이 문제가 해결 되지 않는다.(늘리면 늘리는대로 더 많이 조회함)
- Application에서 조회 조건을 추가하여 한번에 조회 되는 데이터량을 제한하고
- 꼭 필요한 대량의 조회건이 필요하다고 한다면 해당 조회에 따르는 데이터량(결과값을 받아서 저장하고 크기를 확인하고 * 동시 사용예상 인원 + 30%)
- Permanent 영역의 Full GC
- 서비스의 필요에 의하여 지속적으로 JSP를 배포하는 경우가 있다
- 그리고 JSP의 변경을 즉시 반영하도록 (hotdeploy) 설정한 경우 해당 class의 메타데이터가 permanent에 지속적으로 추가되며
- Perm 영역은 기본 컨셉이 GC를 하지 않도록 구성되어 있으므로 결국 가득 차면서 GC가 발생된다.
- JSP가 바뀌면 기존 JSP 정보가 unload 되면 좋겠지만 jsp가 컴파일된 class 파일은 상위 contextLoader가(WAR Context) referrer를 가지고 있기 때문에 Context의 unload(즉 이건 WAR를 내렸다가 올린다는 말이다...WAS재구동해도 되고 WAR만 내렸다 올릴 수 도 있다.)가 되지 않으면 해결이 안된다.
- hotDeploy로 인한 Perm 영역의 누수는 대부분 기존 referrer가 활성 상태로 체크 되어 GC 대상이 못 되는 경우가 많아서 해결이 되지 않는다.
- Full GC가 일어나는 순서는 아래와 같다.
- JSP를 지속적으로 배포한다.
- JSP 안에는 많은 Method가 포함된다. 기존 정보는 그대로 있으면서
- 바뀐 JSP정보가 Permanent 로딩 된다.
- 계속 바뀐다. Perm이 점점 줄어든다. 결국은 없다.
- Full GC (Unloading 이라고 한다)
- Applicaiton이 느린 경우 Full GC
- 순서는 이러하다
- APP가 객체를 만든다.
- APP가 계속 돌고 있다 = GC 대상이 아니다.
- APP가 바보라 처리가 끝이 않난다.
- 해당 APP 호출이 계속있다 = GC는 안되는 메모리에 객체가 계속 생성된다
- Full GC
- 순서는 이러하다
- SQL이 느린 경우 Full GC
- 순서는 이러하다.
- SQL이 느리다
- 1개당 5초 걸린다. (getConnection 부터 recordSet 사용 완료까지)
- 해당 APP의 요청량이 50TPS 이라면 초당 50개 요청인데 DB Pool 꽉 찬다. (Pool 갯수를 늘려봐야 필요없다. 조금 느리게 발생할 뿐)
- DB Pool이 꽉차면 모든 APP는 Connection을 대기한다.
- 모든 대기중인 APP가 메모리를 점유한다 = 아직 끝나지 않았다= GC 대상이 아니다.
- Full GC
- 순서는 이러하다.
- What's new JDK 8
- 2014년 Q1 JDK8이 출시되고 바뀐것이 무엇일까 해서 보았습니다.
- 개발환경 부분에서 새로운 코드스타일과 객체에 대한 지원등에 대한 내용이 추가되어 있습니다만.. 우리(미들웨어 제품)와 큰 관계 없는 이야기로 링크만 추가합니다.
- http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html
- 툴 부분에서
- jedps(정적 의존성 검색-can understand the static dependencies of their applications and libraries) 도구 추가
- pkg(jar,class)의 단순한 포함 내용
- pkg와 연관(참조)된 다른 pkg와 해당 pkg의 내용까지 검색하여 출력
- pkg 의존성을 찾아볼때 사용하면 좋을 것 같음
- a.jar 라는 파일이 있는데 이 jar와 같이 넣어둬야 하는 다른 jar 파일을 찾을때 등
- jedps(정적 의존성 검색-can understand the static dependencies of their applications and libraries) 도구 추가
- HotSpot (http://docs.oracle.com/javase/8/docs/technotes/guides/vm/)
- AES 하드웨어 암호화 지원을 위한 함수지원....별관계 없는
- -Xincgc (incremental gc) 더 이상 지원 안함
- -XX:MaxPermSize 설정이 사라지고 -XX:MaxMetaspaceSize 로 변경되었습니다.
- -XX:PermSize 설정이 사라지고 -XX:MetaspaceSize 로 변경되었습니다.
이상.
작성자가 댓글을 삭제했습니다.
답글삭제조금이나마 도움이 되셨다면 다행입니다. ^^
삭제딱하니 하나의 문서를 참고한건 아니고
예전부터 직원들 교육할때 틈틈히 만들어쓰던 자료에 G1 GC부분만 추가했습니다..
찾아본 문서라고 하면
구글에서 이미지 검색 한것과
jdk 문서중에 hotspot 관련 아티클 http://www.oracle.com/technetwork/articles/java/index-jsp-140228.html 정도 되겠습니다.
나머지는쓰셨듯이 경험치적인 내용이 많으내요
수고하세요~
정말 좋은내용이네요 감사히 잘 읽었습니다.
답글삭제좋은 내용 이네요. 잘 읽었습니다!
답글삭제OOM내용은 정말 핵심만 추려주셨네요. 추천 합니다.
잔ㅉ 감사....
답글삭제와... 찐하게 읽고 갑니다.
답글삭제즐찾 추가해야겠네요 ^^ 감사합니다
좋은글 감사합니다.
답글삭제퍼가요~