2015년 3월 18일 수요일

JAVA8 Permanent 영역은 어디로 가는가

JDK 8이 발표 된지도 좀 되었다.

얼마전에 그 여파로 GC 내용도 좀 정리해봤는데
(http://yckwon2nd.blogspot.kr/2014/04/garbage-collection.html)

포스팅 말미에 Java8 부터 Permanent 영역에 대한 설정

-XX:PermSize=350m / 기동시 Perm사이즈
-XX:MaxPermSize=400m /최대사이즈

두 가지가 사라지고

-XX:MaxMetaspaceSize=
-XX:MetaspaceSize=

가 등장했다.

java 개발자들이 바보가 아니고서야 쓸데 없이 이름을 왜 바꾸었을까 싶었는데

보다 보니 이래저래 Perm 에 대한 전략이 수정된것 같아서 정리해 본다.



java7 까지의 permanent(영속적, 이하 perm) 영역에는  다음과 같은 정보들이 저장되었다.

1. Class 의 Meta정보 (pkg path 정보라고 보면 됨, text 정보)
2. Method의  Meta 정보
3. Static Object
4. 상수화된 String Object
5. Class와 관련된 배열 객체 Meta 정보
6. JVM 내부적인 객체들과 최적화컴파일러(JIT)의 최적화 정보

-Methods of a class (including the bytecodes)
-Names of the classes (in the form of an object that points to a string also in the permanent generation)
-Constant pool information (data read from the class file, see chapter 4 of the JVM specification for all the details).
-Object arrays and type arrays associated with a class (e.g., an object array containing references to methods).
-Internal objects created by the JVM (java/lang/Object or java/lang/exception for instance)
-Information used for optimization by the compilers (JITs)

해깔려들 하시는게 static이 어떻게 저장되느냐 인데
변수의 경우 그냥 값 자체가 perm에 들어갑니다. 만약 Object 라면 Object 자체가 저장되지요
static int i = 1; //the value 1 is stored in the permgen section

요즘의 거의 이런일은 없지만 개발자가 별생각없이 Collection Object를 Static 으로 선언하고 값을 계속 추가하다보면 Perm 이 가득차서 OOM이 발생하기도 했지요...


Class의 경우 JVM기동시 사용되는 모든 Class와 Method Meta 정보가 Perm에 저장되고
Code 상에 아래와 같이 static으로 정의되면 해당 메타 정보도 Perm에 들어가게 되지요
static Object o = new SomeObject(); //the reference(pointer/memory address) is stored in the permgen section, the object itself is not.


결국 Perm에 저장되는 위 여섯가지 중에서 Perm 부족을 일으키는 상대는
1. Static Object의 잘못된 사용 (개발자가 원인)
2. Class, Method Meta data의 증가   (hotDeploy가 원인)

두 가지가 가장 큰 문제였고 Perm 구조를 가지는 Sun JVM  계열 (Open JDK, HP JDK 등)에서는 Perm 누수는 운영에 큰 불편함을 감수하도록 했습니다.

그래서 결국 결정은 이겁니다.

"Permanent 영역을 없에 버리자!!!"

특히나  Oracle의 경우

BEA 라는 회사(Weblogic을 개발했고 자체 JVM 으로 JRockit 을 개발한)를 인수해서 JRockit 을 가지고 있다가 Sun의 JAVA 팀을 먹은 이후로

JRockit의 지향점 (대용량 Heap 지원을 위한 구조, Perm 없음, Survive 영역없음, NEW/OLD 만 있음)을 Java 8에 적용하게 되었고 Perm 영역이라는 개념을 제거해 버립니다.
G1 알고리즘도 지향점은 동일 합니다.

그럼....

거기 저장되던 것들은 어떻게 되는데????
(아래에서 Native 는 Metaspace를 의미함)

1. Class 의 Meta정보 (pkg path 정보라고 보면 됨, text 정보)  --> Native 영역으로 이동
2. Method의  Meta 정보--> Native 영역으로 이동
3. Static Object --> Heap 영역으로 이동
4. 상수화된 String Obejct  --> Heap 영역으로 이동
5. 클레스와 관련된 배열 객체 Meta 정보  --> ???? (아티클 봐서는 모르겠는데 Native로 간듯)
6. JVM 내부적인 객체들과 최적화컴파일러(JIT)의 최적화 정보 --> ???   (아티클 봐서는 모르겠는데 Native로 간듯)


정리하면 java7  까지는
new / survive / old / perm /  native  로 구분했다면
java 8에서는
new / survive / old / metaspace 로 아키텍쳐가 변경되었고

기존의 perm에 저장되어 문제를 유발하던
static obect는 heap으로 옮겨서 GC 대상이 최대한 될 수 있도록 하고 (Final 로 해놓으면 나두 모름)

기타 정말 수정이 될 일 없어보는 정보는
Native(Metaspace) 로 몰아넣고 사이즈는 자동적으로 조정되도록 개선 되었다고 정리하면 되겠습니다.

.....

여기서 개인적으로 두려운것은...

사실 최근에는 Framework의 대중적인 사용 및 개발자 수준향상(?)으로 미친 Static 사용문제는 거의 발생하지 않고 있고
Perm의 가장 큰 문제로 JSP를 지속적으로 HotDeploy 하는 회사에서 발생하는 Perm 누수문제 인데...
(Production 에서 Class 까지 Hotdeploy 하지는 않겠찌?????)

이게... Perm을 Max를 정해놓고 할때는 Perm이 꽉 차면 OOM이 발생
“java.lang.OutOfMemoryError: PermGen space” error 

하고 장애로 확인되면 설명을 해주고 대응방안으로

1. JSP Hot Deploy 하지 마라  (서비스 요건상 해야 되면 어쩔 수 없다면...)

2. Perm을 적절히 늘리자
어쩔 수 없이 JSP hostdeploy를 해야 한다면 늘어나는 추이를 분석해서 최소한 3일 이상 버티도록 = (금요일퇴근, 토,일 은 버텨야 하지 않겠는가) 설정을 늘려준다

3. 정기적으로 재구동
요건상 누수를 안고 가야 하므로 배포 주기나 일정기간 마다 통제된 환경에서 재구동 해주는 운영상의 방법 사용

을 설명해준다.
(Perm 을 GC할 수 있도록 해달라고 땡깡 부리는 사람들이 있는데....제기랄....난 모른다. Context 개념도 없는 사람에게 Perm의 Meta 정보가 쌓이는 메커니즘을 설명 할 방법이 없다)


그런데 이제는 이 문제를 Native 영역으로 옮겨버린단다...
거기다 -XX:MaxMetaspaceSize 설정을 하지 않으면 JVM이 알아서 사이즈를 조정한다고 하는데....

알다시피 JVM 장애시 Native Memory 영역의 누수는 분석이 불가능하다 (dump를 생성해도 Native 영역 정보는 생성 되지 않는다)

이렇게 되면 새로운 어려움이 생기는데...

Core  파일을 생성해서 gdb로 누수 현상에 대해서 분석해야 하는...????

난 모르겠다...  이건 내 수준을 벗어나는 부분이라 어찌될지...
(어짜피 Perm도 분석대상이 아니였으니 그다지 문제가 되지 않을 지도...)


여하간  아래 4개의 값을 어떻게 잡고 갈지가 아직 오리 무중이다.

일단 기본적으로 Perm에 적용하던 기본값 (min 256m, max 512m)으로 잡고 GC정보를 보면서 경험치를 늘려 가는 방법뿐일 듯

  • -XX:MetaspaceSize=<NNN> where <NNN> is the initial amount of space(the initial high-water-mark) allocated for class metadata (in bytes) that may induce a garbage collection to unload classes. The amount is approximate. After the high-water-mark is first reached, the next high-water-mark is managed by the garbage collector
  • -XX:MaxMetaspaceSize=<NNN> where <NNN> is the maximum amount of space to be allocated for class metadata (in bytes). This flag can be used to limit the amount of space allocated for class metadata. This value is approximate. By default there is no limit set.
  • -XX:MinMetaspaceFreeRatio=<NNN> where <NNN> is the minimum percentage of class metadata capacity free after a GC to avoid an increase in the amount of space (high-water-mark) allocated for class metadata that will induce a garbage collection.
  • -XX:MaxMetaspaceFreeRatio=<NNN> where <NNN> is the maximum percentage of class metadata capacity free after a GC to avoid a reduction in the amount of space (high-water-mark) allocated for class metadata that will induce a garbage collection.


참고
https://blogs.oracle.com/poonam/entry/about_g1_garbage_collector_permanent
http://java.dzone.com/articles/java-8-permgen-metaspace
http://www.infoq.com/articles/Java-PERMGEN-Removed




댓글 1개:

  1. jdk1.8 이상에서 jstat gcutil 로 보면, metaspace 영역의 사용률이, 실제 gc log 에 찍히는 사용량과 상이합니다. jdk1.9 이상에서 fix 된것으로 보입니다. 실제 데이터량을 보려면, mbean 에 있는 값으로 추출해야 하네요

    답글삭제

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