JMX를 이용하여 JVM 모니터링 하기
JDK 1.5 부터 JVM에는 Platform MBeanServer가 내장되어 있어 jvm 파라미터로 Platform MBeanServer가 동작하도록할 수 있습니다.
모니터링 대상 프로그램 구동시 다음과 같은 jvm 파라미터를 사용하여 실행합니다.
jvm 파라미터:
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
예제 소스 :
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
public class JMXClient
{
public static void main(String[] args)
{
try
{
// rmi를 이용하여 JVM의 Platform MBean Server에 연결합니다.
JMXServiceURL url = new JMXServiceURL(
“service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi”);
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
MBeanServerConnection mbs_ = jmxc.getMBeanServerConnection();
// OperatingSystemMBean으로 부터 운영체제 정보를 조회합니다.
ObjectName stdMBeanName =
new ObjectName(“java.lang:type=OperatingSystem”);
System.out.println(“System Arch = ”
+ mbs_.getAttribute(stdMBeanName, “Arch”));
System.out.println(“Number of Processors = ”
+ mbs_.getAttribute(stdMBeanName, “AvailableProcessors”));
System.out.println(“OS Name = ”
+ mbs_.getAttribute(stdMBeanName, “Name”));
System.out.println(“OS Version = ”
+ mbs_.getAttribute(stdMBeanName, “Version”));
// RuntimeMBean으로 부터 Runtime 정보를 조회합니다.
stdMBeanName = new ObjectName(“java.lang:type=Runtime”);
System.out.println(“Name = ”
+ mbs_.getAttribute(stdMBeanName, “Name”));
System.out.println(“Spec Name = ”
+ mbs_.getAttribute(stdMBeanName, “SpecName”));
System.out.println(“VM Version = ”
+ mbs_.getAttribute(stdMBeanName, “VmVerion”));
// ClassLoadingMBean 으로 부터 로딩된 클래스 정보를 조회합니다.
stdMBeanName = new ObjectName(“java.lang:type=ClassLoading “);
System.out.println(“Loaded Class Count = ”
+ mbs_.getAttribute(stdMBeanName, “LoadedClassCount”));
System.out.println(“Total Loaded Class Count = ”
+ mbs_.getAttribute(stdMBeanName, “TotalLoadedClassCount”));
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
실행결과 :
System Arch = x86
Number of Processors = 2
OS Name = Windows XP
OS Version = 5.1
Name = 2904@hslim
Spec Name = Java Virtual Machine Specification
VM Version = 1.5.0_03-b07
Loaded Class Count = 3501
Total Loaded Class Count = 3531
JMX를 이용하여 JVM 모니터링 하기 포스트에는 메모리를 모니터링하는 부분이 없습니다. 이번에는 메모리를 모니터링하는 예제를 작성하여 보도록하겠습니다.
1. 메모리 모니터링 MBeans
1.1 java.lang.management.MemoryMBean
Object Name : java.lang:type=Memory
JVM내에서 유일한 인스턴스로 메모리 시스템에 대한 관리 인터페이스를 제공합니다. 이 MBean을 이용하여 전체 메모리 사용 현황을 모니터링 할 수 있습니다. 하지만 Memory Pool 처럼 Peak 메모리 현황은 제공하지 않습니다.
주요 메소드 :
– MemoryUsage getHeapMemoryUsage()
Heap 메모리의 사용 현황을 리턴합니다.
– MemoryUsage getNonHeapMemoryUsage()
Non-Heap 메모리의 사용 현황을 리턴합니다.
– void gc()
GC를 수행합니다.
메모리 시스템은 크게 Heap 영역과 Non-Heap 영역으로 나누어 집니다.
Heap :
Heap 영역은 실행중에 생성되는 데이터들을 위한 영역입니다. 클래스인 스턴스와 배열들이 할당됩니다. 이 영역은 JVM이 구동될 때 생성되며, 객체를 위하여 할당된 Heap 메모리는 gc에 의해서 반환 됩니다.
Non-Heap :
Non-Heap 영역은 JVM에 의하여 관리되는 Heap 이외의 영역을 말합니다. JVM은 스레드들간에 공유되는 “method area”를 가지고 있으며 Non-Heap 영역에 위치합니다.
이곳에는 클래스 별로 런타임 상수 풀, 필드 및 메소드 데이터, 그리고 메소드 및 생성자들의 코드와 같은 자료구조들을 저장합니다.
Heap 영역은 JVM이 구동될 때 생성되며, 논리적으로 Heap의 일부이지만 구현된 JVM은 garbage collect 또는 compact 하지 않습니다.
“method area” 외에 JVM은 내부 프로세싱 및 최적화를 위하여 Non Heap 영역에 있는 메모리를 사용합니다. 예를 들어 JIT 컴파일러는 성능 향상을 위하여 JVM 코드에서 변환된 원시 기계 코드를 저장할 메모리를 필요로 합니다.
1.2 java.lang.management.MemoryPoolMBean
Object Name : java.lang:type=MemoryPool,name=pool’s name
Memory Pool은 JVM에 의하여 관리되는 메모리 리소스입니다. JVM은 하나 이상의 Memory Pool인스턴스를 가지고 있으며, Pool 이름으로 구분합니다. Heap 또는 Non-Heap의 일부분이며 하나 이상의 Memory Manager에 의하여 관리 됩니다. Memory Pool은 실행 중에 JVM에 의하여 생성되거나 소멸될 수 있습니다.
Memory Pool은 JVM에 따라 인스턴스의 수 및 Object 이름이 가변적이므로 Object Name으로 직접적으로 접근을 할 수 없습니다. 리모트에서 모니터링 시에는 MBeanServerConnection의 queryMBeans()메소드를 이용하여 Object Name들을 조회하여 접근해야 합니다.
주요 메소드 :
– List getMemoryManagerNames()
Memory Pool을 관리하고 있는 Memory Manager들의 이름 리스트를 리턴 합니다.
– String getName()
Memory Pool의 이름을 리턴 합니다.
예) Survivor Space, Tenured Gen, Perm Gen, Eden Space, Perm Gen [shared-ro],
Perm Gen [shared-rw], Code Cache
– MemoryUsage getPeakUsage()
JVM이 구동된 후 또는 Peak Usage가 reset 된 후로부터 가장 많이 사용된 메모리 현황을
리턴 합니다.
– MemoryType getType()
Memory Pool의 유형(Heap 또는 Non-Heap)을 리턴 합니다.
– MemoryUsage getUsage()
Memory Pool의 현재 메모리 사용 현황을 리턴 합니다.
– void resetPeakUsage()
Peak 메모리 사용현황을 현재 사용현황으로 초기화합니다.
1.3 java.lang.management.MemoryManagerMBean
Object Name : java.lang:type=MemoryManager,name=manager’s name
Memory Manager는 하나 이상의 Memory Pool들을 관리합니다. Garbage collector는 사용되지 않는(Unreachable) 객체가 점유하고 있는 메모리 영역을 반환하는 역할을 하는 Memory Manager 입니다. JVM은 하나 이상의 Memory Manager를 가지고 있으며 구동 중에 생성되거나 소멸 될 수 있습니다.
주요 메소드 :
– String getName()
Memory Manager의 이름을 리턴합니다.
– List getMemoryPoolNames()
Momory Pool들의 이름을 리스트에 담아 리턴합니다.
1.4 javax.management.MBeanServerConnection
MBeanServer에 대한 로컬 및 리모트 인터페이스를 제공합니다. 리모트에서 Plaform MBean의 인터페이스를 이용하고 자 할 경우는 MBeanServerConnection를 통하여 할 수 있습니다. 물론 MBeanServerConnection은 JMXConnector와 같은 Connector를 통하여 인스턴스를 얻습니다. MBeanServerConnection의 인터페이스 중에 이번 예제에서는 다음과 같은 메소드들을 사용합니다.
주요 메소드 :
– Object getAttribute(ObjectName name, String attribute)
name에 해당하는 ObjectName을 가진 MBean에 대하여 attribute문자열이 가리키는 속성
값을 쿼리 합니다.
– Set queryNames(ObjectName name, QueryExp query)
name 인자가 null이 아닌 경우는 주어진 name이 존재하는지 확인 할 때 주로 사용됩니다.
query 인자는 패턴으로 Object Name을 조회 하고자 할 경우에 사용됩니다.
QueryExp 인터페이스를 구현한 클래스는 ObjectName이 있습니다. ObjectName에 “*”, “?”이
들어간 경우는 QueryExp으로 사용됩니다. “*”는 공백 및 한 문자 이상의 문자열에 해당하고
“?” 는 단일 문자에 해당합니다.
예) MemoryPoole들의 Object Name을 조회
new ObjectName(“java.lang:type=MemoryPool,*”);
2. 소스 코드
MemoryMBean을 이용하여 전체 메모리 공간의 Heap 및 NonHeap의 메모리 사용 현황 및 각각의 MomeryPool에 대해서는 Peak Usage와 Usage 메모리 사용 현황 출력합니다.
import java.lang.management.MemoryUsage;
import java.util.Iterator;
import java.util.Set;
import javax.management.MBeanServerConnection;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeData;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
public class MemoryMonitor
{
public static void main(String[] args)
{
try
{
JMXServiceURL url = new JMXServiceURL(
“service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi”);
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
MBeanServerConnection mbs_ = jmxc.getMBeanServerConnection();
ObjectName stdMBeanName = new ObjectName(“java.lang:type=Memory”);
MemoryUsage musage_ = MemoryUsage.from(
(CompositeData) mbs_.getAttribute(stdMBeanName,
“HeapMemoryUsage”));
System.out.println(“————————————————-“);
System.out.println(“MemoryMBean 정보 출력:”);
System.out.println(“> HeapMemoryUsage-init = ”
+ musage_.getInit()/(1024.f) + ” kbytes”);
System.out.println(“> HeapMemoryUsage-max = ”
+ musage_.getMax()/(1024.f) + ” kbytes”);
System.out.println(“> HeapMemoryUsage-used = ”
+ musage_.getUsed()/(1024.f) + ” kbytes”);
System.out.println(“> HeapMemoryUsage-committed = ”
+ musage_.getCommitted()/(1024.f) + ” kbytes”);
musage_ = MemoryUsage.from(
(CompositeData) mbs_.getAttribute(stdMBeanName,
“NonHeapMemoryUsage”));
System.out.println(“n> NonHeapMemoryUsage-init = ”
+ musage_.getInit()/(1024.f) + ” kbytes”);
System.out.println(“> NonHeapMemoryUsage-max = ”
+ musage_.getMax()/(1024.f) + ” kbytes”);
System.out.println(“> NonHeapMemoryUsage-used = ”
+ musage_.getUsed()/(1024.f) + ” kbytes”);
System.out.println(“> NonHeapMemoryUsage-committed = ”
+ musage_.getCommitted()/(1024.f) + ” kbytes”);
// Query
stdMBeanName = new ObjectName(“java.lang:type=MemoryPool,*”);
Set pools_ = mbs_.queryNames(null, stdMBeanName);
Iterator itr_ = pools_.iterator();
while(itr_.hasNext())
{
Object obj_ = itr_.next();
ObjectName objName_ = (ObjectName) obj_;
// Print Memory Pool Usage
System.out.println(“———————————————-“);
System.out.println(mbs_.getAttribute(objName_, “Name”)
+ ” Pool 정보 출력:”);
System.out.println(“Memory Type = ”
+ mbs_.getAttribute(objName_, “Type”));
System.out.println(“Memory Peak Usage:”);
musage_ = MemoryUsage.from(
(CompositeData) mbs_.getAttribute(objName_, “PeakUsage”));
System.out.println(“> MemoryUsage-init = ”
+ musage_.getInit()/(1024.f) + ” kbytes”);
System.out.println(“> MemoryUsage-max = ”
+ musage_.getMax()/(1024.f) + ” kbytes”);
System.out.println(“> MemoryUsage-used = ”
+ musage_.getUsed()/(1024.f) + ” kbytes”);
System.out.println(“> MemoryUsage-committed = ”
+ musage_.getCommitted()/(1024.f) + ” kbytes”);
System.out.println(“nMemory Current Usage:”);
musage_ = MemoryUsage.from(
(CompositeData) mbs_.getAttribute(objName_, “Usage”));
System.out.println(“> MemoryUsage-init = ”
+ musage_.getInit()/(1024.f) + ” kbytes”);
System.out.println(“> MemoryUsage-max = ”
+ musage_.getMax()/(1024.f) + ” kbytes”);
System.out.println(“> MemoryUsage-used = ”
+ musage_.getUsed()/(1024.f) + ” kbytes”);
System.out.println(“> MemoryUsage-committed = ”
+ musage_.getCommitted()/(1024.f) + ” kbytes”);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
3. 실행 결과
—————————————————
MemoryMBean 정보 출력:
> HeapMemoryUsage-init = 0.0 kbytes
> HeapMemoryUsage-max = 65088.0 kbytes
> HeapMemoryUsage-used = 5964.367 kbytes
> HeapMemoryUsage-committed = 6984.0 kbytes
> NonHeapMemoryUsage-init = 28864.0 kbytes
> NonHeapMemoryUsage-max = 118784.0 kbytes
> NonHeapMemoryUsage-used = 23218.664 kbytes
> NonHeapMemoryUsage-committed = 32608.0 kbytes
—————————————————
Survivor Space Pool 정보 출력:
Memory Type = HEAP
Memory Peak Usage:
> MemoryUsage-init = 64.0 kbytes
> MemoryUsage-max = 448.0 kbytes
> MemoryUsage-used = 64.0 kbytes
> MemoryUsage-committed = 64.0 kbytes
Memory Current Usage:
> MemoryUsage-init = 64.0 kbytes
> MemoryUsage-max = 448.0 kbytes
> MemoryUsage-used = 55.710938 kbytes
> MemoryUsage-committed = 64.0 kbytes
—————————————————
Tenured Gen Pool 정보 출력:
Memory Type = HEAP
Memory Peak Usage:
> MemoryUsage-init = 1408.0 kbytes
> MemoryUsage-max = 60544.0 kbytes
> MemoryUsage-used = 5795.1953 kbytes
> MemoryUsage-committed = 6408.0 kbytes
Memory Current Usage:
> MemoryUsage-init = 1408.0 kbytes
> MemoryUsage-max = 60544.0 kbytes
> MemoryUsage-used = 5795.1953 kbytes
> MemoryUsage-committed = 6408.0 kbytes
—————————————————
Perm Gen Pool 정보 출력:
Memory Type = NON_HEAP
Memory Peak Usage:
> MemoryUsage-init = 8192.0 kbytes
> MemoryUsage-max = 65536.0 kbytes
> MemoryUsage-used = 9642.258 kbytes
> MemoryUsage-committed = 9728.0 kbytes
Memory Current Usage:
> MemoryUsage-init = 8192.0 kbytes
> MemoryUsage-max = 65536.0 kbytes
> MemoryUsage-used = 9642.258 kbytes
> MemoryUsage-committed = 9728.0 kbytes
—————————————————
Eden Space Pool 정보 출력:
Memory Type = HEAP
Memory Peak Usage:
> MemoryUsage-init = 512.0 kbytes
> MemoryUsage-max = 4096.0 kbytes
> MemoryUsage-used = 512.0 kbytes
> MemoryUsage-committed = 512.0 kbytes
Memory Current Usage:
> MemoryUsage-init = 512.0 kbytes
> MemoryUsage-max = 4096.0 kbytes
> MemoryUsage-used = 239.5625 kbytes
> MemoryUsage-committed = 512.0 kbytes
—————————————————
Perm Gen [shared-ro] Pool 정보 출력:
Memory Type = NON_HEAP
Memory Peak Usage:
> MemoryUsage-init = 8192.0 kbytes
> MemoryUsage-max = 8192.0 kbytes
> MemoryUsage-used = 5476.3594 kbytes
> MemoryUsage-committed = 8192.0 kbytes
Memory Current Usage:
> MemoryUsage-init = 8192.0 kbytes
> MemoryUsage-max = 8192.0 kbytes
> MemoryUsage-used = 5476.3594 kbytes
> MemoryUsage-committed = 8192.0 kbytes
—————————————————
Perm Gen [shared-rw] Pool 정보 출력:
Memory Type = NON_HEAP
Memory Peak Usage:
> MemoryUsage-init = 12288.0 kbytes
> MemoryUsage-max = 12288.0 kbytes
> MemoryUsage-used = 5716.2344 kbytes
> MemoryUsage-committed = 12288.0 kbytes
Memory Current Usage:
> MemoryUsage-init = 12288.0 kbytes
> MemoryUsage-max = 12288.0 kbytes
> MemoryUsage-used = 5716.2344 kbytes
> MemoryUsage-committed = 12288.0 kbytes
—————————————————
Code Cache Pool 정보 출력:
Memory Type = NON_HEAP
Memory Peak Usage:
> MemoryUsage-init = 192.0 kbytes
> MemoryUsage-max = 32768.0 kbytes
> MemoryUsage-used = 2383.8125 kbytes
> MemoryUsage-committed = 2400.0 kbytes
Memory Current Usage:
> MemoryUsage-init = 192.0 kbytes
> MemoryUsage-max = 32768.0 kbytes
> MemoryUsage-used = 2383.8125 kbytes
> MemoryUsage-committed = 2400.0 kbytes
[출처] JMX를 이용하여 JVM 모니터링 하기-2 [메모리 모니터링]|작성자 오작두
[출처] JMX를 이용하여 JVM 모니터링 하기 (대박몰 프로젝트) |작성자 그리드맨