2014년 4월 11일 금요일

오랜만에 Garbage Collection 정리

  1. WAS를 다시보려니 JDK 8도 나오고.... 그러고보니 JDK 5 이후에는 JVM Spec을 찾아본적도 없고.... 간만에 6,7,8 한꺼번에 따라가 봅니다.  내용은 주로 Middleware의 성능튜닝과 관련된 HotSpot 관련 정보입니다.
  2. 문서에서는 다음과 같은 내용에 대한 정보를 제공합니다.
    1. JVM 이란?
    2. JVM에서 Heap과 GC란?
    3. GC 의 종류는
    4. GC 적용 전략
    5. What's new JDK 8
  3. JVM이란
    1. JVM의 장점
      1. 멀티 플랫폼, 높은 호환성
        1. 대부분의 OS에 맞는 JVM Binary 제공
      2. 개발 생산성, 잘 다듬어진 JAVA Comp
        1. Component-inheritance, Implement, Over….
      3. 개발/운영의 편의성
        1. 메모리 자동관리 (never malloc/free)
        2. 시스템 자원 자동할당
    2. JVM의 단점
      1. 이 죽일 놈의 속도 (JNI필수, 개선 JIT/hotspot compiler)
        1. 시어머니가 둘이다! (JNI Interface, OS Interface)
      2. 자동화의 단점, 통제가 안 된다
        1. 메모리 정리(GC)는 JVM 맘대로? System.gc()
        2. 공유객체(static obj)는 말 그대로 공유 쓰레기통?
      3. 높은 생산성 대비 막 운영(?), 막 개발(?)의 폐해
        1. 일단 돌아가게 메모리 많이 줘버려! 
        2. 알아볼 수 없는 코드, 1000개 조회 해서 10개 쓴다, 나쁜 유지관리
    3. OS 측면에서 보면  JVM도 Process 일 뿐이다.
      1. JAVA Process는?
    4. JVM입장에서 보면 WAS도 Java Application 일 뿐이다.
      1. JVM위에서 구현된 Application = Web Application Server
      2. 성능과 자기만의 색깔을 위해 도입된 JNI 와 확장 Package들
        1. JNI : Java Native Thread 들, JVM의 취약점인 IO관련 작업을 C로 처리한다
          1. 사용자 요청을 받아들이는 Server Socket 부문
          2. DB Connection을 생성하는 Client Socket 부문
          3. Log Write를 관리하는 File IO부문 
        2. 확장 Package
          1. Open Source 들 중에 괜찮은 Pkg를 선택해서 추가한다.
          2. 추가할때 그냥 하면 빈티 나니까.... 의존성을 높이기 위해... 편의 API를 제공한다.
            1. JMS를  쓰겠다고 Weblogic Intigration API를 사용했다면 Weblogic에 의존적이됨
            2. Session 공유를 하겠다고 JBoss Infinispan을 쓰면 JBoss와 노예계약  (잘 쓰면 득 못쓰면 독)
      3. WAS의 일반적인 버그는 기능 설정과 관련이 있다
      4. WAS의 Critical 버그는 JVM 버그에 의존적이다.
  4.  JVM에서 Heap 과 GC란
    1. Heap
      1. JAVA Object들의 작업 공간 (Sun(oracle) JVM / HP JVM Architecture,  not equal IBM / BEA(ORACLE) jrockit)
        1. Sun JVM 구조
          1. NEW(Eden) : 신규 생성 되는 객체 들이 저장 되는곳  
          2. Survivor 1(From,ss1),2(To,ss2) : Old로 이전 되기 전 Promotion 대상이 되기 위해 거쳐 가는 곳
          3. Old : 오래(?) 사용되어야 하거나 NEW가 부족한 경우 저장되는 공간
          4. Perm(Permanent) : java code, method들의 List,Static Object 들이 저장되는 공간 (메타데이터라고 들 한다), 
          5. Stack : Thread 당 할당된다.(너무 크게 잡으면 메모리 낭비, 적으면 Stack Overflow)   
          6. Native(Other) : JNI관련 시스템 Object들이 사용하는 공간 
            1. !! Xmx 2G로 잡았는데  Top 명령에서 RSS를 보면 2G를 넘는이유는 Stack 과 Native가 잡는 영역 때문이다.
            2. !! 예를들어 기본적으로 Xmx보다 너무 크다면 Xss를 너무 크게 잡으면 늘어난다.
            3. !! 동적으로 Xmx 보다 크게 늘어난다면 File IO 또는 Socket IO 용 버퍼가 크거나 많이 사용되면 늘어날 수 있다.
            4. !! JIT로 인한 증가는 일반적으로 수백Mbyte 이하이다. JIT로 인한 메모리 누수에는 아래와 같이 JIT대상에서 제외 할 수 있다.
              1. -Xjit:exclude={package/class.method|package/class.method}
              2. 또는 파일에 exclude 대상을 리스팅하여 넣고(한줄에 제외할 메서드 하나씩)
                1. .hotspot_compiler  파일을 만들고 안에다가 내용을 넣는다.
                  1. exclude com/amir/SomeClass someMethod  (한줄에 하나씩, Class를 넣어도 되고 Method를 넣어도 된다.)
                2. -XX:CompileCommandFile=/my/excludefile/location/.hotspot_compiler
            5. JNI로 별도로 개발된 모듈의 메모리 누수는 ..... JNI 할아버지를 불러야 한다...

        2. SUN JVM 구조 
          The memory model of the Sun Hotspot JVM
        3. BEA JVM 구조 (Nursery : 보육장 - Sun의 ᅟ Young)
          Heap of the Oracle JRockit JVM
        4. IBM JVM 구조
          The IBM JVM generational heap
        5. Sun JVM G1 구조

          Humongous(엄청난?) : region의 50%가 넘는 크기의 Obj가 생성되면 Humongous region(몇 개의 region을 합함)으로 저장한다.
        6. Sun JVM의 상세 구조
        7. Heap에 저장되는 Object의 구조 
    2. Garbage Collection 
      1. 이란
        1. Referrer가 없는 Object  들을 메모리에서 제거(Minor, MajorGC) 하거나 지속해서 사용할 Object를 Promotion 하여 저장(OLD)
        2. 단편화된 메모리 공간을 조각 모음 하는 작업
          1. Live,reachable Object : 현재 사용 중인 Object로 GC대상이 아니다.
          2. Unreachable Object :  사용하지 않는 Objject의경우 GC 대상이 된다.
          3. 사용중 과 아님의 기준은? : 참조하는 상위클래스가있는가 == 나에게 referrer가 있는가 
          4. Static Object : Static Object로 선언한 객체의 경우 GC 대상이 아니다.
          5. 위와 같은 그림이지만 Root set이라는 개념과 Obj중 상위 클래스를 참조하는 경우라도 상위 클래스의 참조가 없으면 unreachable 이라는것이 설명 가능해서 추가함
      2. 메모리 관리를 위한 기본 컨셉
        1. weak generational hypothesis  (영역에 대한 자신 없는 가설)
          1. 대부분의 객체는 금방 접근 불가능 상태(unreachable)가 된다.
          2. 오래된 객체에서 젊은 객체로의 참조는 아주 적게 존재한다.
        2. 그래서 두 개의 영역으로 쪼갰다
          1. Young 영역(Yong Generation 영역): 새롭게 생성한 객체의 대부분이 여기에 위치한다. 대부분의 객체가 금방 접근 불가능 상태가 되기 때문에 매우 많은 객체가 Young 영역에 생성되었다가 사라진다. 이 영역에서 객체가 사라질때 Minor GC가 발생한다고 말한다.
          2. Old 영역(Old Generation 영역): 접근 불가능 상태로 되지 않아 Young 영역에서 살아남은 객체가 여기로 복사된다. 대부분 Young 영역보다 크게 할당하며, 크기가 큰 만큼 Young 영역보다 GC는 적게 발생한다. 이 영역에서 객체가 사라질 때 Major GC(혹은 Full GC)가 발생한다고 말한다.
        3. 그래도 느리다.
          1. 병렬로 처리 하자
          2. 실시간으로 처리하자
        4. 구조적으로 도저히 방법이 없다. 해결하자
          1. New/Old개념은 가져가되 구조적 영역은 포기
          2. CG First(G1) 알고리즘 제공
      3. 하면
        1. 기본적으로 JVM은 STW (Stop The World) 된다. (GC 튜닝은 STW을 줄이는것이 최종 목표)
      4. 하고 나면 
        1. 필요한 메모리가 정리되고 새로운 Object 생성이 가능하다
      5. 실패하면
        1. 지속적으로 GC를 시도하게 되며, 그동안 STW 상태이므로 사용자는 장애로 판단한다.



      6. 실패하는 이유
        1. 생성해야 할 Object가 너무 큰 거나 Heap이 부족하거나
          1. 생성하고자 하는 Object가 New 영역보다 작을때 : Minor GC를 수행하고 남는 공간이 생성할 공간만 큼 되면 생성한다.
          2. 생성하고자 하는 Object가 New 영역보다 크거나, Minor GC후에도 공간이 없으면 : Old 영역으로 바로 Promotion을 진행한다.
          3. 생성하고자 하는 Object가 Old 영역보다 작고, Old 영역에 여유가 있다면  :  정상적으로 생성하고 진행한다.
          4. 생성하고자 하는 Object가 Old 영역보다 작고, Old 영역에 여유가 없다면  :  Full GC를 수행하고 저장을 시도한다.
          5. Full GC후에도 Old 영역에 여유가 없다면 : OOM 발생하고 객체 생성에 실패한다.
          6. 생성하고자 하는 Object가 Old 영역보다 크면 : OOM 발생하고 객체 생성에 실패한다.
          7. 주의사항
            1. !! OOM이 발생해도 이론상 JVM이 깨지거나 문제가 발생해서는 않된다.(해당 APP의 Exception발생은 정상적인것임)
            2. !! OOM이 발생하면서 JVM에 무한 Full GC가 나거나 Hang 상태로 보이는 이유는 우리가 본 시점의 OOM이 문제가 아니라 OOM이 발생되도록 메모리를 점유하고 있는 그 무엇인가가 문제이다.  (로그를 보고 OOM발생 Exception의 APP를 무조건 의심하면 안된다.)
        2. Object가 지속적으로 Heap을 점유하는 경우
          1. 생성해야 할 Obj의 크기가 크지 않더라도 절대적인 Heap의 여유 공간이 없다면 OOM이 지속적으로 발생한다.
          2. 대표적인 OOM 발생 원인은
            1. Static 객체에 연결한 대량의 정보
              1. 코드상에 Static final로 정의한 사용자 Obj
              2. 세션 객체에 저장한 대용량 Obj
            2. WAS 연계 시스템의 지연
              1. DB Query가 무겁거나 DB에 문제가 있어 응답이 느리면 해당 처리를 위해 Thread가 대기하고 Thread가 사용중이던 Heap의 Object들은 Refere가 유지되므로 OOM 발생이 될 수 있다.
              2. 기타 원격지 연계시스템(결제, 승인, 실명확인 등)의 경우에도 동일한 증상으로 인하여 OOM 발생위험이 있다.
            3. 마구잡이 개발
              1. DB Cache 객체를 사용하는데 적절한 통제 없이 마구 저장할 경우 절대적인 heap 사이즈가 부족해진다.
              2. 잘못된 코드로 대량 객체를 생성하고 해당 객체를 JSP등에서 세션 범위에서 사용하는 경우 사용자가 늘어나면 OOM이 발생한다.
    3. Promotion 이란  
        1. New 영역은 사용자 호출에 의해 처음으로 생성된 객체(Object = 실 메모리를 점유하는)가 만들어지는 1차 작업 공간입니다.
        2. 이 공간의 영역은 제한적(-Xmn)이므로 지속적으로 사용될 것 같은 Object들은 절차에 따라 Old 영역으로 넘어가게 되며 이 절차를 Promotion이라고 합니다.
        3. GC에서 사용되는 단어 설명
          1. Mark : Object가 현재 사용되고 있는지에 대한 검사, 기준은 Referrer가 있는가, 즉 이 Object가 Applicaiton Code상에서 연결된 것이 아직 사용되고 있는가 (Minor GC)
          2. Sweep : Mark 대상이 된 Object를 불용처리합니다, 실제 메모리를 지우는 작업은 아니고 File 삭제시 FAT에서 정보만 지우듯이 Object 메모리 주소번지만 제거됩니다. (Minor GC)
          3. Compact
            1. 조각모음이라고 보시면 됩니다. 이 작업은 평소에는 수행 되지 않으며 Full GC 시에 수행됩니다. 이 작업의 기본컨셉은 STW(Stop The World)
            2. sweep 된 Object들이 차지하던 공간을 다른 Object들을 이동시켜 메꾸어 단편화를 정리합니다.
            3. JVM의 Heap 공간의 메모리 관리는 Object가 생성 될때 해당 Object가 차지 하는 공간이 연속된 주소공간으로 확보되어야 합니다.
            4. 100M의 여유공간이 있더라도 단편화 되어 연속된 빈 공간이 없다면 Compact를 통하여 정리하게 됩니다.  (Major GC)
        4. Promotion의 진행 순서는 아래와 같이 진행됩니다.  (GC 알고리즘에 따라 조금씩 다르지만 기본 사항으로 이해 하시면 됩니다. G1은 완전 다름...)

          1. 그림은 처음 영역을 표현 합니다 Eden(NEW) 영역에 각자 다른 크기의 Object들이 존재합니다.
            From, To 영역은 Eden 영역의 작은 Object가 OLD로 바로 Promotion이 되지 않고 몇 번의 이동 작업(MaxTenuringThreshold)을 거치다가 sweep 되도록 유도합니다.
            그림에서는 Eden에 있던 일부 객체들은 From 영역으로 이동 되어 있습니다.
        5. 마이너 GC가 발생 되는 순간의 그림입니다.
          1. Yonung 영역에서 Referer가 없는 object는 제거 되고 사용 중인 경우 Survivor space로 이동 됩니다. (From,To의 이동은 통째로 이동 됩니다.  그래서 두 개의 영역의 사이즈는 동일하고, survivor로 들어가는 Obj도 이번 차수에 이동될 영역으로 들어갑니다.)
          2. From 영역에서 Referer가 없는 객체가 제거 됩니다.
          3. From, to를 수 회 왔다 갔다(-XX:MaxTenuringThreshold=32) 한 Obj가 아직 referer가 살아 있다면 Old로 넘어갑니다.
          4. From에 저장되어 있던 obj 중에서 아직 Old로 넘어갈 카운트가 안된 경우 To로 이동 됩니다. (MaxTenuringThreshold 수치까지 왔다리 갔다리 합니다.)
          5. Old에는 앞서 단계를 거치고 살아남은 Obj가 저장되어 있습니다.
        6. 마이너 GC가 수행 되고 나면 다음과 같이 정리 된 상태가 됩니다.

        7. 아래는 Compaction(Full GC)의 그림입니다.
          1. Referer가 없는 Obj는 Mark 됩니다.
          2. Mark 된 Obj는 Sweep 됩니다.  (실제 삭제 작업은 이루어 지지 않으나 Stack에 정보를 삭제)
          3. 주변의 Obj를 순차적으로 이동하여 fragment를 제거합니다.
          4. 이 작업이 수행되는 동안은 STW 입니다.
  5. Full GC & GC Type
    1. 란?
      1. Old 영역 부족, Perm 영역 부족에 의하여 발생
      2. JVM이 상대적으로 느린 것은 어쩔 수 없고, 결국 STW(Stop The World)의 최소화가 관건이다.
    2. 가 일어나면?
      1. 처리 되는 기간 동안 JVM은 동작을 멈춘다 
      2. 일반적인 기준으로 JVM의 가용율에서 STW는 5% 미만으로 통제하여야 한다. 
      3. 24시간 운영시 ( 86400초 * 0.05 = 4320초 ) 최대 72분 내로 통제 (웹서비스 에서는 이것도 너무 러프 하다)
      4. 다만 연속적이면 무조건 안 된다. (말도 안되기는 하지만 72분 동안 WAS가 정지하면 누가 이해 하겠어?)
      5. 간격으로 이야기 하자면 허용 Duratiuon Time이 5Sec 라면  되도록 균등해야 하므로 
        1. 하루의 5%를 GC허용 시간으로 가정하면 = 72분 
        2. GC허용 시간을 GC수행 허용 시간(duration) 나누면 =  864회(72분 / 허용시간 = 4320sec / 5sec) 이하 발생해야 하므로
        3. 1일 기준 가용시간 = 86400sec - 4320sec  =  859680sec ( = 1일 - 허용GC시간)  
        4. 허용 발생회수를 나누면  = 859680sec / 864회 = 995초(약 17분) 간격을 평균 간격으로 이야기 할 수 있다.
    3. 실패하면
      1. VM입장에서는 메모리를 정리해야 다음 처리가 가능하므로 무한 Full GC가 반복된다. =지속적 STW
      2. APP 입장에서는 Out Of Memory Exception이 발생하고 처리가 중지된다.
      3. 간혹 JVM이 죽어버리는 경우도 발생한다. 
      4. 결과적으로 Full GC가 발생하는 것은 인정하지만 실패는 발생하면 안된다.
    4. CMS에서의 Full GC(CMS는 Full  GC를 예방하는 방식이지만 한번 발생하면 대책이 없다...)
      1. CMS는 기본 구조에서 Old 영역에 대한 Compaction 작업을 수행하지 않습니다. (메모리 단편화가 증가함)
      2. JAVA의 Heap 구조에서 Obj는 연속된 메모리 주소공간이 확보 되지 않으면 객체 생성에 실패합니다.  (단편화 되어 100M가 있어봐야 Obj는 생성에 실패할 수 있다)
      3. 이러한 이유로 Old 영역이 실 사용량 보다 적거나 너무 타이트하게 할당될 경우 CMS 무한 Full GC가 발생할 수 있습니다.
      4. 해결하기 위한 컨셉은 아래와 같은 옵션으로 접근한다.
        1. -XX:SurvivorRatio : From, To 영역의 사이즈를 키워서 작은 obj(단편화 원인)를 Old로 Promotion 되지 않도록 유도(From, To 에서 GC 되기를 기대할 뿐 늘린다고 해결 되지 않는다)
        2. -XX:MaxTenuringThreshold : Promotion 조건인 From, To 영역의 이동 카운트값을 늘려서 더 많이 이동(왔다리 갔다리)하게 만들어서 Old로 Promotion 되지 않도록 유도
        3. -XX:CMSInitiatingOccupancyFraction : CMS 동작의 트리거를 좀 더 빨리 동작하도록 한다, 예를 들어 사용율 60%부터 동작한다면 30%로 줄여서 미리 CMS를 구동해서 단편화를 제거한다(compact를 하는것이 아니기때문에)
        4. -XX:+UseCMSInitiatingOccupancyOnly :항상 CMS를 점진적으로 사용하도록 합니다. 
    5. STW 개선방안은?  (GC 알고리즘)
      1. JVM의 GC에서 메모리를 정리하는 것은 JVM 내에 있는 APP(HotSpot)이다. 이 APP는 Thread로 동작한다.
        1. Serial GC(-XX:+UseSerialGC) : 기본적은 GC는 1개의 Core가 있는 시스템등에 적용하기 위한 GC로 PC Application등에는 가능하나 서버에는 적용해서는 안된다.
        2. Parallel GC(-XX:+UseParallelGC) : JDK6에서는 기본 GC, N 개의 GC Thread가 있으면 전체 STW Time은 1/n 이다 (이론적으로 하지만 좀 더 걸린다.) 
        3. Parallel Old GC(-XX+UseParallelOldGC) :  JDK5 update 6부터 제공되었으며 Parallel GC와 동일하지만 Old 영역 GC 구조가 변경되어  Mark-Summary-Compaction 단계를 거친다. (XX:+UseParallelGC를 적용하면 기본 활성화 됨)
        4. Concurrent Mark&Sweep GC(-XX:+UseConcMarkSweepGC) : N개의 Thread + 계속적인 GC를 통하여 STW이 발생하지 않도록 최소화 하는 방법도 있다. (CMS라고 부른다)
        5. Minor GC발생시 Old도 덤으로 GC하는 Train GC(-Xincgc) = Incremental GC 도 있다.  (컨셉은 훌륭한데....거의 사용 안함), JDK8에서 Deprecate 됨
        6. 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
          1. CMS가 Old 영역의 Fragement에 대응하지 못하고 무한 Full GC를 수행하는 문제점이 있고 이를 해결 하기 위한 방안으로 제시되었다  
          2. Multi Process + Lage Memory 시스템을 대상으로 한다.  (Heap 1~2G 시스템에 적용하면 Overhead로 오히려 성능 문제가 발생할 수 도 있을 듯....이건 추정임)
          3. New,Old 영역에 연속된 메모리 주소(물리적) 에 대한 구조적 개념을 버리고 메모리 영역을 통으로 관리 함으로써 프로모션이라는 것 자체가 없어졌다. 
          4. 전체 메모리를 Region(지방,부문) 이라고 부르는 대략 1Mbyte단위(-XX:G1HeapRegionSize=size)의 블럭형식 단위로 분할 하고 이 영역에 객체를 생성한다. 
            1. JDK 8 기본 사이즈는 JVM이 상황 봐서 정의 한답니다. The default region size is determined ergonomically based on the heap size.
            2. JDK 6 u26에서는  init heap size / 2048 이였다.  (아마도 JDK8도 동일할 듯)
          5. New,Old 가 없으므로 Promotion이 없으며 메모리 할당 제어를 Evacuation(이바큐에이션-소개,비우다) 이라고 부른다. (개념은 비슷함)
          6. 내부적으로 GC동작에 대한 기준은 STW 시간에 대한 목표치를 가지고 동작한다.  –XX:MaxGCPauseMillis=<n>, default value = 200ms.  (거의 실시간으로 봐야할 듯)
          7. 목표치 시간에 대한 GC가 될 수 있도록 내부적으로 추정해서 동작한다.(알고리즘은 잘 모르겠어요....) 즉 짧게 지정할 수 록 GC가 빨리 동작하고 = 자원을 많이 먹는다
          8. 기존 Old 영역에서 참조하는 New 영역의 객체 정보(referrer)를 관리하던 Card Table이 전체 region에 대한 객체 referrer에 대한 관리형태로 변경되었다.(Remembered Sets 이라고 한다)
          9. 2014년 Q1 현재 G1을 공식적으로 지원하는 JVM은 ORACLE JDK(Sun JDK) 뿐이다.
          10. 현재 G1 GC로그를 분석할 수 있는 툴은 개발되어 있지 않다. log를 보고 직접 분석해야 한다. (-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps  -XX:+PrintHeapAtGC)
            1. -XX:+UnlockExperimentalVMOptions 옵션은 JVM에 실험적 옵션을 활성화 하는 옵션이다.
            2. JDK7 update4 부터는 위 옵션을 넣지 않아도 된다.  (jdk6, jdk7 u3이하에 사용)
          11. Option and Default Value
            Description
            -XX:+UseG1GCUse the Garbage First (G1) Collector
            -XX:MaxGCPauseMillis=nSets 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=nPercentage 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=nRatio of old/new generation sizes. The default value is 2.
            -XX:SurvivorRatio=nRatio of eden/survivor space size. The default value is 8.
            -XX:MaxTenuringThreshold=nMaximum value for tenuring threshold. The default value is 15.
            -XX:ParallelGCThreads=nSets 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=nNumber of threads concurrent garbage collectors will use. The default value varies with the platform on which the JVM is running.
            -XX:G1ReservePercent=nSets 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=nWith 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.
          12. G1의 동작 (기존과 다르게 G1은 GC가 시작되면 Young region 정리(MinorGC)후 Evacuation 과 Compaction(MajorGC)가 연속으로 진행된다.)
            1. 기동
              1. G1으로 설정한 JVM은 New/SS/Old 로 구분되는 물리적 메모리 구분 없이 region으로 불리는 메모리 블럭을 구성하면서 기동 된다.
              2. 논리적으로 New/SS/Old는 존재 하지만 물리적으로 주소번지로 구분 되지 않는다.
              3. 초기 Object들은 임의의 region에 생성되며 해당 obj의 referrer 정보는 Remember set(전체 Heap에서 5%수준을 사용)에 저장된다.
              4. region 크기보다 큰 obj가 생성되어야 한다면 몇 개의 region에 걸쳐서 Object를 생성하게 되고(인접해 있는) 이러한 region 집합을 Humongos라고 부른다. 이 정보도 remember set에 저장된다.
            2. ᅟYoung GC = Minor GC
              1. 수행단계 (Marking -> Remarking -> Evacuation )
                1. Concurrent Mark : Single Thread로 진행, 정지없음, remember set의 referrer 정보를 참조하여 live가 아닌것만 빠르게 마킹한다.
                2. Remarking : 병렬로 진행, 정지 없음, Region마다 live obj의 밀도(전체 Obj중에서 Live 비율)를 계산하고 만약 UnReachable Obj만 있을 경우 Region 자체를 폐기한다.
                3. Evacuation : 병렬로 진행, 정지시간 있음. region의 live obj 밀도가 낮은 region에 대하여 Evacuation을 실행한다.
                  1. 정리 대상 region에서 live obj는 새로운 region(Promotion age에 따라 ss 또는 old region) 으로 복사 된다.
                  2. 논리적으로 Serviver 영역(SS)인 다른 region으로 복사 거나 Live Obj만 있는 region(논리적으로 Old영역)으로 복사된다.
                  3. 이동 후에 unReachable obj만 있는 region은 폐기된다.
                  Icon
                  Marking 작업은 snapshopt-at-the-begining(SATB) Marking 알고리즘을 사용한다. 
                  1. GC 실행시 remember set의 reference 정보를 기준으로 모든 Live Obj의 reference를 추적하고, referrrer가 없는 Obj를 mark한다.
                  2. 이때 Mark 작업은 병행수행(Concurrent)되므로 remember set이 지속적으로 변경된다(비교기준이 없다) 그렇기 때문에 GC 시작시의 remeber set의 snapshot을 기준으로 변경된 내용에 대해서 Marking 작업을 수행하게 된다.(전체 Scan이 아니므로 빠르다)
              2. 설명
                1. 시간이 지나면 referrer가 없는 obj들이 증가 되게 된다. (remeber set을 참고하여 지속적으로 region에 대하여 Concurrent Mark가 수행됨)
                2. Minor GC는 실시간으로 지속적으로 수행 되나 전체에 대한 GC는 아니고 region단위로 수행되며 Live Obj가 없는 region은 폐기함으로써 메모리를 정리하게 된다.  (전체적으로는 메모리가 정리되지만 단편화가 증가한다.)
            3. Old GC = Major GC
              1. 수행단계 (Evacuation -> Compaction)
                1. Evacuation
                  1. yong GC의 Evacuation과 동일한 방식으로 시작한다.
                    1. 앞서 Scan한 정보를 기반으로 region의 live obj 밀도가 낮은 region에 대하여 Evacuation을 실행한다.
                    2. 정리 대상 region에서 live obj는 새로운 region(Promotion age에 따라 ss 또는 old region) 으로 복사 된다.
                    3. 논리적으로 Serviver 영역(SS)인 다른 region으로 복사 거나 Live Obj만 있는 region(논리적으로 Old영역)으로 복사된다.
                    4. 이동 후에 unReachable obj만 있는 region은 폐기된다.
                  2. 이때 앞서 진행하면서 Survivor region으로 지정된 영역과 Old region으로 지정된 영역이 이동 되어 메모리 앞쪽으로 정리된다.
                2. Compaction
                  1. Evacuation을 거치고 나면 전체 메모리에서 Live Obj가 있는 young region에 해당하는 영역들이 듬성듬성(단편화) 존재하게 된다.(위의 그림 우측)
                  2. 메모리가 부족하거나 GC 수행시간이 설정보다 길어질 것으로 판단되면 GC를 다시 수행하면서 region들을 앞쪽으로 이동 시켜 정리하게 된다.
              2. 설명
                1. Evacuation작업은 Minor(yongGC)와 동일한 작업을 수행하지만 정리된 region을 이동시키는 부분이 조금 다르다.
                2. G1의 Compaction 작업은 region 단위로 이루어지며
                3. 해당 region을 참조하는 Thread가 잠시 느려지기는 하지만  
                4. 전체적으로 서비스는 진행 중이므로 STW은 발생하지 않는다. (아주 짧은 정지는 있다)
  6. Garbge Collection strategy
    1. Throughput VS Response Time trade-off
      1. 단위 시간당 일량(Throughput)을 증가시키려면 GC소요시간=횟수를 줄여야 하고 GC를 줄이기 위해 CMS나 병렬GC를 사용하는 경우 CPU자원을 더 사용하게 되고 
      2. 정해진 자원에서 더 많은 CPU를 사용한다는 것은 응답시간의 증가와 같으므로 Response time과 반비례 하게 된다.(라고 문서상에 표현한다.)
        1. 하지만 최근에는 워낙 장비의 CPU Power들이 좋기 때문에 GC를 줄이기 위해 복잡한 GC 알고리즘을 사용한다고 Response time과 반비례 하지는 않는다.
        2. GC를 실시간으로 처리하는 경우  비교적(GC를 한꺼번에 하는 것 보다는) Response Time이 증가하지만 성능상 영향은 미미하므로 무시할 수준이라고 판단하면 됩니다.
      3. 쓰루풋 중심 튜닝 (WEB Service는 요고이 중요하다)
        1. 응답시간을 조금 늘이더라도 GC가 최대한 발생하지 않도록 한다.
        2. Parallel Algorithm 
          1. 여러 개 Thread 가 Full GC 를 최대한 빨리 끝낸다.
          2. CMS에 비하여 안정성 높음 
        3. Concurrent Algorithm (CMS)
          1. 여러 Thread + 실시간으로 Mark & Sweep = Full GC 최대한 방지
          2. CPU 자원 가장 많이 사용
          3. Parallel과 Serial에 비하여 안정성에 문제가 있다. Object Type(small or big size)JVM 및 OS 마다 조금씩 다르다
        4. G1
          1. 메모리 영역을 순차적으로 관리하지 않고 1Mbyte 단위의 Region이라는 블럭들을 사용하고 Compact를 배제 시킴으로써
          2. GC의 발생을 줄이는 것 과 함께 GC 수행시간을 개선시킴
          3. 메모리상의 region에 대한 연관관계(referrer)를 관리하기 위한 Overhead가 있으므로  작은 heap 영역을 사용하는 경우 오히려 좋지 않을 수 도 있다(Xmx 2G 이하 등)
      4. 응답성능 중심 튜닝 
        1. Default(Serial) Algorithm
          1. Full GC가 발생하더라도 속도가 중요하다.
          2. 단독 APP의 경우 기본이 차라리 나을때가 있다.
    2. 업무 형태에 따른 튜닝
      1. 포털 VS 업무시스템
        1. 포털
          1. 다수의 사용자가 짧은 방문시간을 유지하며 많은 페이지를 호출한다.
          2. 이 경우 JVM에서는 Old 보다는 New영역에 대한 객체 생성 관리가 중요하다.
          3. 최대한 Old영역으로의 Promotion을 적게 하도록 하여 Full GC로 인한 정지시간이 작아지도록 하고
          4. 작은 Obj들이 old에 저장됨으로써 발생하는 단편화가 작아지도록 적용해야 한다.
          5. 관련 옵션
            1. -XX:NewRatio=3 (default 2) : heap에서 New영역에 대한 비율 정의,  New 영역을 크게(Old보다는 작아야 한다)하여 더 많은 객체를 저장하도록 하여 Old Promotion을 줄인다.(Ratio 가 아닌 -Xms= -Xmx= -XX:NewSize= -XX:MaxNewSize= -XX:SurvivorRatio=  를 조합해도 된다.)
            2. -XX:SurvivorRatio=2 (default 8): New영역에서 Survivor 영역에 대한 비율, 기본 사이즈보다 크게 해서 Old Promotion을 줄인다.
            3. -XX:TargetSurvivorRatio=90 : SS 영역정리 기준을 기존 50%에서 90%로 올려 Old Promotion을 줄인다.
            4. -XX:MaxTenuringThreshold=64 (default 15/CMS = 4): SS에서 새로 만들어졌고 , 작은 크기의 Obj들이 SS1,SS2를 더 많이 채류하도록 하여 Old Promotion을 줄인다.
        2. 원장조회, 매출실적 조회
          1. 정해진 인원이 오랜시간동안 대량의 데이터를 조회
          2. 일반적으로 비정형화 된 데이터에 대한 조회를 수행하는 경우 New 영역의 사용빈도가 낮다.
          3. New 영역보다 큰 데이터 사이즈가 빈번하게 저장되므로 주로 Old로 바로 프로모션되는 케이스가 많다.
          4. 이러한 경우 실시간 계열의 GC는 큰 효용성이 없다.
          5. Old 영역을 크게 잡아주고 GC는 단순하게 하는것이 좋다(그래도 single 보다는 Para를 적용한다. CMS는 비추천)
          6. 대량 장표 조회는 WAS에서는 어렵고 최근에는 BI로 거의 넘어갔으며  Bigdata 쪽으로 트렌드를 가지고 간다.
  7. CASE Study
    1. Mass traffic web site Heap Tuning 
      1. JVM Option
        Icon
        Open Market Front Web Site Heap
        -server / 서버운영에 맞도록 자동설정, 최대한 class loading이 처리 된 다음 구동 되도록 한다. 부팅 시간은 오래 걸리지만 서비스 aging time이 짧다
        -Xms2700m / 최소 Heap
        -Xmx2700m / 최대 Heap
        -XX:NewRatio=3 / Heap에서 NEW의 비율
        -Xss128k / 스텍사이즈
        -XX:SurvivorRatio=2 / Edne에서 SS영역의 비율
        -XX:TargetSurvivorRatio=90 / SS 영역의 90%까지만 유지하도록 노력
        -XX:InitialCodeCacheSize=64m / JIT 코드케쉬영역 지정(Native Area)
        -XX:ReservedCodeCacheSize=128M / JIT 코드케쉬영역 최대사이즈 할당 (Native Area)
        -XX:PermSize=350m / Perm사이즈
        -XX:MaxPermSize=400m /최대사이즈 (일반적으로 PermSize와 동일하게 하지만 지속적인 증가를 모니터링하기 위해 좀 더 크게 잡았음)
        -XX:MaxTenuringThreshold=64 64회 SS1,2를 이동하면 OLD로 프로모션
        -XX:+UseParallelGC : 병렬GC
        -XX:ParallelGCThreads=6 : Thread수
        -XX:-UseAdaptiveSizePolicy : New/SS 비율 자동변경 중지
        -XX:+DisableExplicitGC : System.gc()를 통한 수동 GC 트리거 방지  -XX:+ExplicitGCInvokesConcurrent 도 있다. CMS에서 GC 트리거를 방지한다.
        아래 것들은 덤프 분석을 위한
        -XX:+HeapDumpOnCtrlBreak
        -XX:+HeapDumpOnOutOfMemoryError
        -verbosegc
        -XX:+PrintGCDetails
        -XX:+PrintGCTimeStamps
        -XX:+DisableExplicitGC
        -Xloggc:/log/weblogics/${SERVER_NAME}/${SERVER_NAME}_gc_`date +'%y%m%d%H%M%S'`"
      2. 설명
        New Old 비율은 성능에 큰 영향을 줍니다. 일반적으로 비율은 2, 3 에서 이론적 수치가 가장 잘 나옵니다.
        다만 실제 Production에서는 다양한 형태의 Object들이 생성되기 때문에 이론적 TEST결과와 부합되지 않으므로 비율 설정에 따른 모니터링 결과를 반영해야 합니다. 
        Icon
        NEW영역 과 OLD영역 의 계산
        NewRatio 3 = NEW Generation : Old Generation = 1:3
        2700M 이면 2700 / 4 = 675M(NEW) : 2025M(OLD)
        SurvivorRatio 2 = New에서 Eden 의 SS1,2와의 비율 = SS1 : SS2 : Eden = (1:1):2
        675M 이면 675 / 4 = 168M 168M : 504M = (84M:84M):504M

    2. Back Office 에서 Full GC
      1. 기업의 back office applicaiton이라 함은 ERP나 벌크성 데이터를 수집 가공하는 업무가 많음
      2. 이런 경우 너무 많은 SQL 결과값에 의하여 Heap 영역이 부족함으로 인한 문제가 종종 발생함
      3. Full GC가 일어나는 순서는 아래와 같다.
        1. 너무 많은 SQL 결과값 조회
        2. New 영역에 저장 되기에도 크다 OLD로 그냥 프로모션 된다.
        3. 아직 처리가 안 끝났다 = 레퍼런스가 살아 있다. GC대상이 되지 못한다.
        4. Full GC
      4. 해결방안은
        1. Heap 크기를 늘려서는 대부분 이 문제가 해결 되지 않는다.(늘리면 늘리는대로 더 많이 조회함)
        2. Application에서 조회 조건을 추가하여 한번에 조회 되는 데이터량을 제한하고 
        3. 꼭 필요한 대량의 조회건이 필요하다고 한다면 해당 조회에 따르는 데이터량(결과값을 받아서 저장하고 크기를 확인하고 * 동시 사용예상 인원 + 30%)
    3. Permanent 영역의 Full GC
      1. 서비스의 필요에 의하여 지속적으로 JSP를 배포하는 경우가 있다
      2. 그리고 JSP의 변경을 즉시 반영하도록 (hotdeploy) 설정한 경우 해당 class의 메타데이터가 permanent에 지속적으로 추가되며
      3. Perm 영역은 기본 컨셉이 GC를 하지 않도록 구성되어 있으므로 결국 가득 차면서 GC가 발생된다.
      4. JSP가 바뀌면 기존 JSP 정보가 unload 되면 좋겠지만 jsp가 컴파일된 class 파일은 상위 contextLoader가(WAR Context) referrer를 가지고 있기 때문에 Context의 unload(즉 이건 WAR를 내렸다가 올린다는 말이다...WAS재구동해도 되고 WAR만 내렸다 올릴 수 도 있다.)가 되지 않으면 해결이 안된다.
      5.  hotDeploy로 인한 Perm 영역의 누수는 대부분 기존 referrer가 활성 상태로 체크 되어 GC 대상이 못 되는 경우가 많아서 해결이 되지 않는다.
      6. Full GC가 일어나는 순서는 아래와 같다.
        1. JSP를 지속적으로 배포한다.
        2. JSP 안에는 많은 Method가 포함된다. 기존 정보는 그대로 있으면서 
        3. 바뀐 JSP정보가 Permanent 로딩 된다.
        4. 계속 바뀐다. Perm이 점점 줄어든다. 결국은 없다.
        5. Full GC (Unloading 이라고 한다)
    4. Applicaiton이 느린 경우 Full GC
      1. 순서는 이러하다
        1. APP가 객체를 만든다.
        2. APP가 계속 돌고 있다 = GC 대상이 아니다.
        3. APP가 바보라 처리가 끝이 않난다.
        4. 해당 APP 호출이 계속있다 = GC는 안되는 메모리에 객체가 계속 생성된다
        5. Full GC
    5. SQL이 느린 경우 Full GC
      1. 순서는 이러하다.
        1. SQL이 느리다 
        2. 1개당 5초 걸린다.  (getConnection  부터 recordSet 사용 완료까지)
        3. 해당 APP의 요청량이 50TPS 이라면 초당 50개 요청인데 DB Pool 꽉 찬다.  (Pool 갯수를 늘려봐야 필요없다. 조금 느리게 발생할 뿐)
        4. DB Pool이 꽉차면 모든 APP는 Connection을 대기한다.
        5. 모든 대기중인 APP가 메모리를 점유한다 = 아직 끝나지 않았다= GC 대상이 아니다.
        6. Full GC
  8. What's new JDK 8
    1. 2014년 Q1 JDK8이 출시되고 바뀐것이 무엇일까 해서 보았습니다.
    2. 개발환경 부분에서 새로운 코드스타일과 객체에 대한 지원등에 대한 내용이 추가되어 있습니다만.. 우리(미들웨어 제품)와 큰 관계 없는 이야기로 링크만 추가합니다.
    3. http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html
    4. 툴 부분에서
      1. jedps(정적 의존성 검색-can understand the static dependencies of their applications and libraries) 도구 추가
        1. pkg(jar,class)의 단순한 포함 내용
        2. pkg와 연관(참조)된 다른 pkg와 해당 pkg의 내용까지 검색하여 출력
        3. pkg 의존성을 찾아볼때 사용하면 좋을 것 같음
          1. a.jar 라는 파일이 있는데 이 jar와 같이 넣어둬야 하는 다른 jar 파일을 찾을때 등
    5. HotSpot (http://docs.oracle.com/javase/8/docs/technotes/guides/vm/)
      1. AES 하드웨어 암호화 지원을 위한 함수지원....별관계 없는
      2. -Xincgc  (incremental gc) 더 이상 지원 안함
      3. -XX:MaxPermSize 설정이 사라지고 -XX:MaxMetaspaceSize  로 변경되었습니다.
      4. -XX:PermSize 설정이 사라지고 -XX:MetaspaceSize  로 변경되었습니다.
        Icon
        JDK 8 Deprecated and Removed Options
        -Xincgc
        Enables incremental garbage collection. This option was deprecated in JDK 8 with no replacement.
        -Xrunlibname
        Loads the specified debugging/profiling library. This option was superseded by the -agentlib option.
        -XX:CMSIncrementalDutyCycle=percent
        Sets the percentage of time (0 to 100) between minor collections that the concurrent collector is allowed to run. This option was deprecated in JDK 8 with no replacement, following the deprecation of the -XX:+CMSIncrementalMode option.
        -XX:CMSIncrementalDutyCycleMin=percent
        Sets the percentage of time (0 to 100) between minor collections that is the lower bound for the duty cycle when -XX:+CMSIncrementalPacing is enabled. This option was deprecated in JDK 8 with no replacement, following the deprecation of the -XX:+CMSIncrementalMode option.
        -XX:+CMSIncrementalMode
        Enables the incremental mode for the CMS collector. This option was deprecated in JDK 8 with no replacement, along with other options that start with CMSIncremental.
        -XX:CMSIncrementalOffset=percent
        Sets the percentage of time (0 to 100) by which the incremental mode duty cycle is shifted to the right within the period between minor collections. This option was deprecated in JDK 8 with no replacement, following the deprecation of the -XX:+CMSIncrementalMode option.
        -XX:+CMSIncrementalPacing
        Enables automatic adjustment of the incremental mode duty cycle based on statistics collected while the JVM is running. This option was deprecated in JDK 8 with no replacement, following the deprecation of the -XX:+CMSIncrementalMode option.
        -XX:CMSIncrementalSafetyFactor=percent
        Sets the percentage of time (0 to 100) used to add conservatism when computing the duty cycle. This option was deprecated in JDK 8 with no replacement, following the deprecation of the -XX:+CMSIncrementalMode option.
        -XX:CMSInitiatingPermOccupancyFraction=percent
        Sets the percentage of the permanent generation occupancy (0 to 100) at which to start a GC. This option was deprecated in JDK 8 with no replacement.
        -XX:MaxPermSize=size
        Sets the maximum permanent generation space size (in bytes). This option was deprecated in JDK 8, and superseded by the -XX:MaxMetaspaceSizeoption.
        -XX:PermSize=size
        Sets the space (in bytes) allocated to the permanent generation that triggers a garbage collection if it is exceeded. This option was deprecated un JDK 8, and superseded by the -XX:MetaspaceSize option.
        -XX:+UseSplitVerifier
        Enables splitting of the verification process. By default, this option was enabled in the previous releases, and verification was split into two phases: type referencing (performed by the compiler) and type checking (performed by the JVM runtime). This option was deprecated in JDK 8, and verification is now split by default without a way to disable it.
        -XX:+UseStringCache
        Enables caching of commonly allocated strings. This option was removed from JDK 8 with no replacement.
이상.

댓글 7개:

  1. 작성자가 댓글을 삭제했습니다.

    답글삭제
    답글
    1. 조금이나마 도움이 되셨다면 다행입니다. ^^

      딱하니 하나의 문서를 참고한건 아니고

      예전부터 직원들 교육할때 틈틈히 만들어쓰던 자료에 G1 GC부분만 추가했습니다..

      찾아본 문서라고 하면

      구글에서 이미지 검색 한것과

      jdk 문서중에 hotspot 관련 아티클 http://www.oracle.com/technetwork/articles/java/index-jsp-140228.html 정도 되겠습니다.

      나머지는쓰셨듯이 경험치적인 내용이 많으내요

      수고하세요~

      삭제
  2. 정말 좋은내용이네요 감사히 잘 읽었습니다.

    답글삭제
  3. 좋은 내용 이네요. 잘 읽었습니다!
    OOM내용은 정말 핵심만 추려주셨네요. 추천 합니다.

    답글삭제
  4. 와... 찐하게 읽고 갑니다.

    즐찾 추가해야겠네요 ^^ 감사합니다

    답글삭제

본 블로그의 댓글은 검토후 등록됩니다.