이론도 중요하지만 경험도 못지않게 중요하다. 5장에서는 JVM최적화 경험에 대해 공유한다.

위의 사진을 잘 기억하고 아래 내용을 읽자.
해당 문제들은 과거의 문제들이며, 현재 최신 JDK기술로는 바로 해결되기도 하는 문제가 있다. 하지만, 그때 상황에 맞추어 그때 소프트웨어 및 하드웨어를 유지한 채 해결하는 방법을 알아본다.
스펙의 웹 사이트 하드웨어 시스템 이다.
위의 상황에서 페러렐 컬렉터를 활용하여 일시 정지시간보다는 처리량에 중점을 두고 있었음
힙 메모리를 너무 크게 잡아 회수하고 재활용하는 데 너무 오래 걸리는 것이 문제
이전 시스템에서는 2G, 32bit 시스템이였지만, 현 상황보다 응답시간이 빠르다고 한다. (더 짧게 GC가 돌아서 더욱 더 반응속도가 빠름)
배포 전략을 다르게하여 배포할 수 있다.
현 상황에서는 1번 째 방법을 활용하고 있다. 1번으로 상황을 해결하고자 한다면 ZGC나 셰넌도어와 같은 저지연 가비지 컬렉터를 선택해야 한다. 또는 전체 GC를 제어하는 방법이 있다. 사용자가 없는 시간대에 전체 GC를 수행하도록 스케줄링 한다.
전체 GC빈도를 제어하고자 한다면 구세대(Old Generation)이 안정되어야 한다. 너무 오래 생존하는 객체가 없어야 한다는 것이다
참고) 이런 거대 어플리케이션의 힙덥프는 10G를 가볍게 넘긴다. 제대로 만들었다해도 분석이 쉽지않다. 프로덕션에서 관리할 수 있는 운영환경이 필요하다.
다른 방식으로는 12G라는 데이터를 논리적 클러스터로 나누어 배포할 수 있다. 논리적으로 클러스터를 나누고, 앞단에 로드밸런서를 두어 어플리케이션의 요청을 나누는 것이다.
해당 책에서는 PM을 사용하고 있고, 이를 통해 가상 머신을 직접 나누어 클러스터로 배포하지만, 현재는 Cloud 또는 k8s를 통해 클러스터화를 진행하는 것을 권장
이런 논리 클러스터를 통한 배포 및 CMS 가비지 컬렉터를 활용해 응답속도를 높일 수 있다. (저지연 CMS 가비지 컬렉터는 추후 G1GC, 셰넌도어, ZGC의 등장으로 인해 Fade out 된다.)
위의 환경에서 오버플로가 발생했으며, 메모리가 부족해서 힙덤프를 생성해 내지 못했다.
힙덤프를 보지 못하여, jstat을 활용해 상황을 지켜봤더니 가비지 컬렉션은 자주 일어나지 않았다. 또한 에덴, 생존자 공간, 구세대, 신세대 그리고 힙 메모리까지 모두 안정적이였다. 따라서 시스템 로그를 출력해 보았을 때 다이렉트 메모리가 부족하여 메모리 오버플로가 발생한 것을 볼 수 있었다. (CometD 1.1.1 프레임워크가 다이렉트를 이용하는 연산을 많이 수행)
-XX:MaxDirectMemorySize를 활용하여 크기를 조절할 수 있다.
10분단위로 80Mb의 파일을 메모리로 읽어야 하는데 이 때 100만 개 의상의 HashMap<Long, Long>객체를 만들어 냈다. 그러면 마이너 GC가 100만 개 넘는 객체를 검사하느라 STW가 500밀리초로 늘어났다.
원인은 다음과 같다.
파뉴 컬렉터는 복사 알고리즘을 활용하였다. 따라서 복사 비용이 늘어나고, STW가 급격하게 늘어난 것이다.
GC최적화를 통해 이를 해결하고자 한다면 생존자 공간을 제거하거나(-XX:SurvivorRatio=65536, -XX:MaxTenuringThreshhold=0), 살아남은 객체를 즉시 구세대로 옮기는 방법(-XX:AlwaysTenure)이 있을 것이다. 하지만 올바른 해법은 프로그램 자체를 고치는 것이다.