반응형



Out Of Memory 오류로 추정했을시에 발생 시점


참고:http://www.nextree.co.kr/p3878/


개발이 완료되어 사용자 테스트 혹은 인수인계 단계에 많이 발생

(부하를 주는 작업 때문?)


Exception in thread “main”: java.lang.OutOfMemoryError: PermGen space (실제 해당 로그에 남아있던 에러)


Class나 Method 객체를 PermGen space에 할당하지 못하는 경우 발생하며 애플리케이션에서 너무 많은 class를 로드할 때 발생한다. 


주로 잘못된 설계/구현에 의해 발생한다. -XX:PermSize, -XX:MaxPermSize Option을 이용하여 오류를 수정하기도 한다.


(-XX:PermSize, -XX:MaxPermSize Option 을 주어 하루정도 톰캣이 뻗지않은건 확인)



Perm Gen space에서 발생한 오류에 대해 대응하기 전에 Perm Generation 영역에 대해서 알아보면 


Permanent Generation은 young과 old를 구분하는 Generational Collector 방식인 HotSpot JVM 중 한 영역으로


객체의 생명 주가기 길다고 판단되는 객체들을 이 영역에 할당하여 GC대상에서 제외를 하기 위해서 만들어진 영역이다. 


주로 자바의 Class 객체들이나 문자열에 속한 String 객체들이 위치한다.


일반적으로 Class의 로딩은 시스템의 Class path에 의해서 로드된 Class 객체들과 에플리케이션 내 구현으로 다이나믹하게 로드되는 class들이 있는데 


주로 문제는 애플리케이션 내 로직으로 다이나믹 하게 생성되는 Class들에 의해서 발생된다. 


최근에 많이 사용되는 Spring, MyBatis등과 같은 프레임워크 등이 이와 같은 방식을 취하고 있다. 


때문에 위 프레임워크들을 사용할 경우 OOME(Out Of Memory Error)발생에 주의를 해야 한다.


- 기본적인 메모리 설정 방식

JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1024m -Xmx1024m -XX:NewSize=512m 

-XX:MaxNewSize=512m -XX:PermSize=512m -XX:MaxPermSize=512m -XX:+DisableExplicitGC"


* 참고 : JVM 메모리 옵션


-Xms<size> : Java Heap의 최초 크기(Start Size)를 지정한다. Java Heap은 -Xms 옵션으로 지정한 크기로 시작하며 

최대 -Xmx옵션으로 지정한 크기만큼 커진다. 


Sun HotSpt JVM 계열에서는 최초 크기와 최대 크기를 동일하게 부여할 것을 권장한다. 크기의 동적인 변경에 의한 오버 헤들를 최소화하기 위해서이다.


-Xmx<size> : Java Heap의 최eo 크기(Maximum Size)를 지정한다. -Xms 옵션으로 지정한 크기로 시작하며 최대 -Xmx옵션으로 지정한 크기만큼 커진다. 

Sun HotSpt JVM 계열에서는 최초 크기와 최대 크기를 동일하게 부여할 것을 권장한다. 크기의 동적인 변경에 의한 오버 헤들를 최소화하기 위해서이다.


-XX:PermSize=<size> : Permanent Generation의 최초 크기를 지정한다. Permanent Generation의 최대 크기는 MaxPermSize옵션에 의해 지정된다. 

많은 수의 Class를 로딩하는 Application은 크기의 Permanent Generation을 필요로 하며, 

Permanent Generation의 크기가 작아서 Class를 로딩하지 못하면 Out of Memory Error가 발생한다.


-XX:MaxPermSize=<size> : Permanent Generation의 최대 크기를 지정한다. Permanent Generation의 시작 크기는 PermSize옵션에 의해 지정된다. 

많은 수의 Class를 로딩하는 Application은 PermSize와 MaxPermSize옵션을 이용해 Permanent Generation의 크기를 크게 해주는 것이 좋다. 

Permanent Generation의 크기가 작을 경우에는 Out of Memory Error가 발생한다.


-XX:NewSize<size> : 객체가 생성되어 저장되는 초기공간의 Size로 Eden+Survivor 영역


출처: http://javafactory.tistory.com/328 [FreeLife의 저장소]


MaxPermSize는 -Xmx 로 지정한 메모리 용량과 별도로 할당된다. 

즉, -Xmx가 256m 이고, -XX:MaxPermSize가 256m 이라면, 최대 512m이 할당될 수 있다는 것이다.


출처: http://linuxism.tistory.com/286 [linuxism]


Jconsole 도구를 이용한 메모리 설정법 

http://joont.tistory.com/42


메모리 옵션


앞에서도 설명하였듯이 JVM 튜닝의 대부분의 메모리 튜닝이고 그중에서도 JVM 메모리 튜닝은 매우 중요하다. 


결국 Full GC 시간을 줄이는 것이 관건인데, 큰 요구 사항만 없다면, 전체 Heap Size는 1G 정도가 적당하다. 


그리고 New대 Old의 비율은 서버 애플리케이션의 경우 1:2 비율이 가장 적절하다. 그리고 PermSize는 class가 로딩되는 공간인데, 


배포하고자 하는 애플리케이션이 아주 크지 않다면 128m 정도면 적당하다. 

(보통 256m를 넘지 않는다. 256m가 넘는다면 몬가 애플린케이션 배포나 패키징에 문제가 있다고 봐야 한다.)


그리고 heap size는 JVM에서 자동으로 늘리거나 줄일 수 가 있다. 


그래서 -Xms와 -Xmx로 최소,최대 heap size를 정할 수 있는데, Server 시스템의 경우 항상 최대 사용 메모리로 잡아 놓는 것이 좋다. 


메모리가 늘어난다는 것은 부하가 늘어난다는 것이고, 부하가 늘어날때 메모리를 늘리는 작업 자체가 새로운 부하가 될 수 있기 때문에, 같은 값을 사용하는 것이 좋다.


이렇게 JVM 메모리를 튜닝하면 다음과 같은 옵션이 된다.


-Xmx1024m –Xms1024m -XX:MaxNewSize=384m -XX:MaxPermSize=128m


이렇게 하면 전체 메모리 사용량은 heap 1024m (이중에서 new가 384m) 그리고 perm이 128m 가 되고, 


JVM 자체가 사용하는 메모리가 보통 300~500m 내외가 되서 java process가 사용하는 메모리 량은 대략 1024+128+300~500 = 대략 1.5G 정도가 된다.


출처: http://bcho.tistory.com/788 [조대협의 블로그]


PermGen 메모리를 설정하지 않았을경우 디폴트 값은 80메가 정도


현재 서버 컴퓨터의 MAX Heap Memory 는 7.4기가 ( Xms,Xmx를 4096 정도까지 사용가능 )


프로젝트 4개를 호스트만 변경해서 사용한다고 했을때


디폴트 메모리가 적게 잡혀있어서 문제가 일어났던것으로 파악되어서,


적정설정을 할수있다기보다는 컴튜터 사양에 맞는 최상의 옵션을 설정해두고 


부하가 늘어날때 메모리를 늘리는 작업 자체가 새로운 부하가 될 수 있기 때문에, 같은 값을 사용하는 것이 좋다는 내용을 참고하여


적정 옵션을 설정후에


OOME가 추후에 발생하는지 확인하는 방법이 제일 최선일것같다.


찾아본 허용된 옵션중 최상 옵션


JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms3072m -Xmx3072m -XX:NewSize=384m 

-XX:MaxNewSize=384m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC"


Xms, Xmx를 동일하게 세팅하는 이유


- Xms로 init 메모리를 잡고, committed 도달할 때까지 Used용량이 점차 증가하는데, 

committed에 도달시 메모리 추가할당시 시스템 부하발생 (WAS가 몇 ms가량 멈출 가능성 있음)


- 메모리 용량은 init < used < committed < max 


- 보통 운영시스템에서 Xms와 Xmx를 동일하게 지정하는 이유는 init와 max사이에서 used 메모리가 committed까지 사용하게 되면, 

  신규 메모리 공간을 요구하는데 이 때 약 1초가량 jvm이 메모리 할당 중 멈춰버리는 경우가 있다. 

  그래서 Xms와 Xmx를 동일하게 주고 메모리를 확보한 상태에서 jvm을 기동시키곤 한다. 


출처 : http://javaslave.tistory.com/23




JVM 이란?

Java Virtual Machine 의 줄임말 이며 Java Byte Code를 OS에 맞게 해석 해주는 역할을 합니다. Java compiler는 .java 파일을 .class 라는 Java byte code로 변환 시켜 줍니다. Byte Code 는 기계어가 아니기 때문에OS에서 바로 실행되지 않습니다. 이때 JVM은 OS가 ByteCode를 이해할 수 있도록 해석 해줍니다. 하지만 JVM의 해석을 거치기 때문에 c언어 같은 네이티브 언어에비해 속도가 느렸지만 JIT(Just In Time)컴파일러를 구현해 이점을 극복했습니다. Byte Code는 JVM 위에서 OS상관없이 실행된다. 이런 점이 Java의 가장 큰 장점이라고 할수 있습니다. OS에 종속적이지 않고 Java 파일 하나만 만들면 어느 디바이스든 JVM 위에서 실행 할 수 있습니다. JVM은 크게 Class Loader, Runtime Data Areas, Excution Engine 3가지로 구성되어 있고 자세한 설명은 아래에 이이서 하겠습니다.

JVM 구조

Class Loader

RunTime 시점에 클래스를 로딩하게 해주며 클래스의 인스턴스를 생성하면 클래스 로더를 통해 메모리에 로드하게 됩니다.

Runtime Data Areas

JVM이 프로그램을 수행하기 위해 OS로 부터 별도로 할당 받은 메모리 공간을 말하며, Runtime Data Areas는 크게 5가지 영역으로 나눌 수 있습니다.

Execution Engine

Load된 Class의 ByteCode를 실행하는 Runtime Module이 바로 Execution Engine입니다. Class Loader를 통해 JVM 내의 Runtime Data Areas 에 배치된 바이트 코드는 Executin Engine에 의해 실행되며, 실행 엔진은 자바 바이트 코드를 명령어 단위로 읽어서 실행합니다.

최초 JVM 이 나왔을 당시에는 Interperter방식(한 줄씩 해석하고 실행)이였기 때문에 속도가 느리다는 단점이 있었지만 JIT complier 방식을 통해 이 점을 보완했습니다. JIT는 ByteCode를 어셈블러 같은 NativeCode로 바꿔서 실행이 빠르지만 역시 변환하는데 비용이 발생합니다. 이 같은 이유 때문에 JVM은 모든 코드를 JIT Compiler 방식으로 실행하지 않고 Interpreter 방식을 사용하다 일정한 기준이 넘어가면 JIT Compiler 방식으로 실행합니다.


출처:https://medium.com/@lazysoul/jvm-%EC%9D%B4%EB%9E%80-c142b01571f2

반응형

+ Recent posts