본문 바로가기

WEB_Programming

JVM Tuning


서버 제품에서 더 낳은 성능을 내기 위한 방법으로 JVM 파라미터의 설정파일을 조정함으로 해서 가능하다. 특히 이것은 메모리 사용과 가비지 컬렉션과 연관이 있다.

Heap size

JVM의 모메리 할당은 -X옵션을 이용하여 설정 가능하고, Resin 과 같은 서버는 이러한 JVM 위에서 동직하게 된다. 즉 서버 프로세스를 실행할때 메모리를 설정할 수 있다. (the exact options may depend upon the JVM that you are using, the examples here are for the Sun JVM).

JVM option passed to Resin Meaning
-Xms initial java heap size (초기 자바 힙 메모리 크기)
-Xmx maximum java heap size (최대 자바 힙 사이즈)
-Xmn the size of the heap for the young generation  (young generation을 위한 자바 힙 메모리 크기)

ex) Resin startup with heap memory options
unix> bin/httpd.sh -Xmn100M -Xms500M -Xmx500M
win> bin/httpd.exe -Xmn100M -Xms500M -Xmx500M
install win service> bin/httpd.exe -Xmn100M -Xms500M -Xmx500M -install

이것은 서버 사이트 자바 애플리케이션의 최소 -Xms와 최대 -Xmx 힙 크기를 동일하게 잡아주는 것으로 좋은 습관이라 하겠다.

효과적인 가비지 컬렉션을 위해서 Xmn 값은 -Xmx 값보다 작아야 한다.

힙 사이즈는 사용하는 프로세스 메모리의 크기를 한정하지는 않는다.

top이나 taskmanager과 같은 OS툴을 이용하여 만약 자바프로세스를 모니터링한다면 아마도 -Xmx에 의해서 지정된 메모리 용량을 초과하는 것을 보게 될 것이다. -Xmx 으로 heap size를 제한하면, 자바는 각 쓰레드를 위한 스텍 메모리를 포함한 다른 메모리 영역또한 잡는다. 보통 VM이 전체 메모리를 소비할때 -Xmx의 값을 초과하는 것은 일반적인 사항은 아니다.

Garbage collection

(thanks to Rob Lockstone for his comments)

핵심적으로 수행되고 있는 GC쓰레드는 2개가 있다. 하나는 매우 경량의 쓰레드로 "littel"이며, 주로 heap의 Eden generation (잘 알려진 Young)에 존재한다. 다른것으로 Full GC쓰레드로 객체의 공간을 할당할때 충분한 메모리가 없다면, 전체 힙 영역을 방분하여, Eden에서 older generation으로 이동된 객체를 수집한다.

만약 메모리 릭이 발생하거나, 부적격하게 할당된 힙이 있다면, 결국 older generation에서 Full GC 쓰레드가 수행되도록 한다. 이 프로세스가 처리되는 동안 Resin(WEB Application Server)은 요청에 대해서 응답하지 않고, 백업을 수행하게 된다.

Eden generation을 위한 할당량은 -Xmn을 이용하여 지정가능하며, older generation을 위한 할당량은 -Xmx 에서 -Xmn을 뺀 값으로 할당된다. 일반적으로 Eden이 너무 크거나 GC가 메모리를 찾아내는데 너무 오랜 시간이 걸릴것 같으면 메모리 영역을 다시 잡아주면 된다.

함께 봐야할내용

Stack size

VM내에서 각 쓰레드는 stack 을 갖게 된다. 스택 크기는 가질 수 있는 쓰레드의 수를 제한할 수 있으며, 너무 큰 스택이 잡혔거나, 각 쓰레드에서 out of memory가 발생된다면, 그것들을 위해서 더 큰 메모리가 필요하게 된다.

Resin은 실행 스크립느에서 2048k의 메모리를 잡을 수 있다. 이 크기는 대부분의 환경에서 적합한 수치라 할 수 있다.

JVM option passed to Resin Meaning
-Xss the stack size for each thread 각 쓰레드가 가질수 있는 스택 크기

-Xss는 스택 크기를 결정한다. -Xss1024k로 지정이 가능하다. 만약 스텍 크기가 너무 작으면 결국 class java.lang.StackOverflowError 에러가 발생하게 된다.

몇몇 사람들은 리눅스의 OS에서는 스택 크기를 변경이 필요하다고 말한다. ulimit를 호출하여 이것을 조정하도록 하고 잇다. 이 명령은 /etc/profile에 있다.

Limit thread stack size on Linux
ulimit -s 2048

Monitoring the JVM

JDK 5에는 JVM을 모티너링 하기위한 유용한 툴을 몇가지 제공하고 있는데, 이것에 대한 문서는 Sun website에서 찾아볼 수있다. JDK 5 이전 버젼은 jvmstat tools 에서 확인해보라.

가장 유용한 툴은 jconsole인데 이것은 Resin 문서의 Administration 에서 상세하게 알아볼 수 있다.

jconsole
win> ./httpd.exe -Dcom.sun.management.jmxremote
unix> bin/httpd.sh -Dcom.sun.management.jmxremote

 ... in another shell window ... 

win> jconsole.exe
unix> jconsole

Choose Resin's JVM from the "Local" list.

jps와 jstack또한 유용한 툴인데 이것은 현재 모든 쓰레드의 스택을 트레이싱 할수 있도록 해준다. 결과에 대해서 상세한 해석을 원한다면 Resin 문서에 잇는  Troubleshooting 부분을 참조하자 

jps and jstack
# jps
12903 Jps
20087 Resin
# jstack 20087
Attaching to process ID 20087, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 1.5.0-beta2-b51
Thread 12691: (state = BLOCKED)
 - java.lang.Object.wait(long) (Compiled frame; information may be imprecise)
 - com.caucho.util.ThreadPool.runTasks() @bci=111, line=474 (Compiled frame)
 - com.caucho.util.ThreadPool.run() @bci=85, line=423 (Interpreted frame)
 - java.lang.Thread.run() @bci=11, line=595 (Interpreted frame)


Thread 12689: (state = BLOCKED)
 - java.lang.Object.wait(long) (Compiled frame; information may be imprecise)
 - com.caucho.util.ThreadPool.runTasks() @bci=111, line=474 (Compiled frame)
 - com.caucho.util.ThreadPool.run() @bci=85, line=423 (Interpreted frame)
 - java.lang.Thread.run() @bci=11, line=595 (Interpreted frame)

...