JMX ํด, ์๋ฐ ์ดํ๋ฆฌ์ผ์ด์ ๋ชจ๋ํฐ๋ง ๋๊ตฌ
by JiwonDev#1. JMX ( Java Management Extensions )
JDK 1.5๋ถํฐ ํฌํจ๋ ๋ฐํ์ ์ดํ๋ฆฌ์ผ์ด์ ์ํ ๋ชจ๋ํฐ๋ง API์ด๋ค.
JMX๋ฅผ ํตํด ๋ฆฌ์์ค๋ฅผ ๊ด๋ฆฌํ๋ ค๋ฉด MBean (Managed Bean)๋ฅผ ์์ฑํด์ผํ๋ค. ์์์ MBean์ผ๋ก ๊ฐ์ธ ์ธ๋ถ์์ API๋ก ์ค์ , ๋ฐ์ดํฐ์์ง, ์๊ฒฉ์ ์ด๋ฑ์ ํ ์ ์๊ฒ ๋ง๋ค์ด์ค๋ค.
JMX Connectors๋ฑ์ผ๋ก MBean Server์ ์ ๊ทผํด์ ์ฌ์ฉํ๋ฉด ๋์ง๋ง, JConsole, JVisualVM๋ฑ ์ด๋ฏธ JMX๋ฅผ ์ด์ฉํด ๊ตฌํ๋ ๋ชจ๋ํฐ๋ง ๋๊ตฌ๊ฐ ์๊ธฐ ๋๋ฌธ์ ๊ตณ์ด ์ง์ ๋ง๋ค์ด ์ฌ์ฉํ ํ์๋ ์๋ค. ๋๋ต์ ์ผ๋ก ์ด๋ป๊ฒ ๋์๊ฐ๋์ง๋ง ์์๋ณด๊ณ ๋์ด๊ฐ๋๋ก ํ์.
@ JMX์ ๋์์๋ฆฌ
JMX์ MBean์ ์ฌ์ฉํ๋ ค๋ฉด javax.management ์์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ์ด์ฉํ๋ฉด ๋๋ค.
๋จ, MBean์ ๊ตฌํํ ์ธํฐํ์ด์ค ์ด๋ฆ์ ๋ฐ๋์ 'XXXMBean'์ผ๋ก ์ด๋ฆ์ ์ง์ด์ผ JMX๊ฐ ์ธ์ํ๋ค.
// MBean interface name = implementation class name + MBean.
public interface HelloMBean {
public void setMessage(String message);
public String sayHello();
}
// MBean ์ธํฐํ์ด์ค ๊ตฌํ์ฒด
public class Hello implements HelloMBean {
private String message = null;
public Hello() {
this.message = "Hello JMX!";
}
public Hello(String message) {
this.message = message;
}
@Override
public void setMessage(String message) {
this.message = message;
}
@Override
public String sayHello() {
return "JMX Message ::: " + this.message;
}
}
JMX๋ MBean์ ์ด์ฉํ์ฌ ์์์ API ์๊ฒฉ์์ ์กฐ์ข , ๋ชจ๋ํฐ๋ง ํ๋ ๋๊ตฌ์ด๋ค. ์๋ฐ์ฝ๋๋ก ์์ฑํ MBean์ ์ดํ๋ฆฌ์ผ์ด์ ๋ด๋ถ์ ์๋ MBeanServer์ ๋ฑ๋กํด์ผ ํ๋ค. ์ด๋ ๋ฑ๋ก๋ MBean๋ค์ ๊ด๋ฆฌํ๋ JMX Agent ์ญํ ์ ์ํํ๊ฒ ๋๋ค.
์ด๋ ๊ฒ MBeans์ ์ํด ์์์ด ๊ด๋ฆฌ๋๋๋ก ๊ตฌํํ๋ฉด, MBeanServer (JMX Agent)์ ์ํด ๊ด๋ฆฌ๋์ด์ง๊ฒ ๋๋ค.
์ฐธ๊ณ ๋ก JMX Agent์๋ MBeanServer๋ฟ๋ง ์๋๋ผ ๋ค์ํ ๋น ๊ด๋ฆฌ ์๋น์ค๋ค์ ํฌํจํ๊ณ ์๋ค.
@ ์์
>> HelloAgent ์คํ
HelloAgent is running...
>> HelloClient ์คํ
HelloDomain
JMImplementation
JMX Message ::: ๋ฐฉ๊ฐ๋ฐฉ๊ฐ~~
๐ HelloAgent (JMX Agent) ์ฝ๋
import java.rmi.registry.LocateRegistry;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
public class HelloAgent {
private MBeanServer mbs = null;
public HelloAgent() {
// ๋๋ฉ์ธ๋ช
์ ๋ฌธ์์ด๋ก ๋ฐ์ MBeanServer ์์ฑ
mbs = MBeanServerFactory.createMBeanServer("HelloDomain");
// MBean์ ์ธ์คํด์ค ์์ฑ
Hello helloMBean = new Hello();
try {
LocateRegistry.createRegistry(7777);
JMXServiceURL serviceUrl =
new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:7777/hello");
// HelloWorld MBean Name ์ ์
// ๋๋ฉ์ธ๋ช
: NAME=VALUE,,,,
ObjectName helloMBeanName = new ObjectName("HelloDomain:name=helloMBean");
// helloName์ผ๋ก helloBean์ ๋ฑ๋ก
mbs.registerMBean(helloMBean, helloMBeanName);
// Client์์ ์ ์ํ๋๋ก ์ปค๋ฅํฐ ์์ฑ ๋ฐ ์์
JMXConnectorServer connector =
JMXConnectorServerFactory.newJMXConnectorServer(serviceUrl, null, mbs);
connector.start();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* JMX ์์ด์ ํธ Main์ ManagementFactory ํด๋์ค์ getPlatformMBeanServer ()
* ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ํ๋ซํผ์ ์ํด ์์ฑ๋๊ณ ์ด๊ธฐํ ๋ MBean Server๋ฅผ ๊ฐ์ ธ ์ค๋ ๊ฒ์ผ๋ก ์์.
*/
public static void main(String argv[]) {
new HelloAgent();
System.out.println("HelloAgent is running...");
}
}
๐ HelloClient ์ฝ๋
import javax.management.JMX;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.RuntimeErrorException;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
public class HelloClient {
public static void main(String[] args) {
foo();
}
public static void foo() {
try {
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:7777/hello");
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
// MBeanServerConnection ์ทจ๋
MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
// Get domains from MBeanServer
String domains[] = mbsc.getDomains();
Arrays.sort(domains);
for (String domain : domains) {
System.out.println(domain);
}
// JMX Agent์์ ๋ฑ๋กํ ObjectName ์์ฑ
ObjectName helloMBeanName = new ObjectName("HelloDomain:name=helloMBean");
// MBeanServerConnection์ ํตํด MBean์ ์ง์ ์ ๊ทผํ๋ ๋์
// MBean์ ๋ํ ์ ์ฉ Proxy ์์ฑ
HelloMBean hello = JMX.newMBeanProxy(mbsc, helloMBeanName, HelloMBean.class, true);
// MBean์ ๋ฉ์๋๋ฅผ ์๊ฒฉ ํธ์ถ
hello.setMessage("๋ฐฉ๊ฐ๋ฐฉ๊ฐ~~");
System.out.println(hello.sayHello());
} catch (RuntimeErrorException e) {
System.out.println("Error --->" + e);
e.printStackTrace();
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}
}
}
#2. ์๋ฐ ๋ชจ๋ํฐ๋ง ์ ํธ๋ฆฌํฐ
์ธ๋ถ ๋๊ตฌ๋ค๋ ๋ง์ด ์์ง๋ง, Oracle JDK์ Open JDK ๋ฐฐํฌํ์๋ ์๋์ ๊ฐ์ ์ ํธ ํ๋ก๊ทธ๋จ๋ค์ด ๋ด์ฅ๋์ด ์๋ค.
- JConsole (jconsole.exe)
- Java VisualVM (jvisualvm.exe) - JDK 8๊น์ง๋ ๋ด์ฅ, JDK 9๋ถํฐ๋ ๋ณ๋๋ก ์ค์นํด์ผํจ.
- Java Mission Control (jmc.exe)
JConsole๊ณผ VisualVM์ด ๊ธฐ๋ณธ ๋๊ตฌ๋ผ๊ณ ์๊ฐํ๋ค๋ฉด, JMC๋ ์ค๋ผํด์์ ์ ๊ณตํ๋ ์๋ฐ ๋ชจ๋ํฐ๋ง ๋๊ตฌ์ด๋ค.
- Diagnostic Command Tool (jcmd.exe)
Java8๋ถํฐ ์ ๊ณต๋๋ ๋ถ์๋๊ตฌ๋ก, ํ์ฌ๋ ์ฌ์ฉํ์ง ์๋ ๊ธฐ์กด์ ์๋ฐ JVM ์ง๋จ ๋๊ตฌ๋ค์ ํตํฉํ ํํ์ด๋ค.
(jps, jmap, jstack, jinfo ๋ฑ)
#3. Java Flight Recorder
JDK 8๋ถํฐ ์คํ์ ์ผ๋ก ์ถ๊ฐ๋ ๊ธฐ๋ฅ์ผ๋ก, ๋ ๋ฆฝํ ํ๋ก๊ทธ๋จ(.exe)์ด ์๋๋ผ JDK์ ๋ด์ฅ๋ ๋๊ตฌ์ด๋ค.
@ JMC์์ JFR์ ์ฌ์ฉํ๋๋ฐ ์ด๊ฑด ๋ฌด์จ๋ง์ด์ฃ ?
Java Mission Control(JMC)๋ ์์์ ์ค๋ช ํ ๋๋ก ์๋ฐ ์ฑ์ ๋ํ ์ค์๊ฐ ์ํ ์ ๋ณด๋ฅผ ํ์ธํ๋ ๋๊ตฌ์ด๋ค.
JFR์ ๊ฒฝ์ฐ ์ด๋ ๊ฒ ์ค์๊ฐ์ผ๋ก ์์ง๋ ์ ๋ณด๋ฅผ ์ ์ฅํ์ฌ ๋ฌธ์ ๋ฐ์์ ์์ธ ๋ถ์ ๋ฐ ๋ฌธ์ ๋ฐ์์๊ธฐ๋ฅผ ๋ถ์ํ๋๋ฐ ๋์๋๋ ์ ๋ณด๋ฅผ ํ์ผ(.jfr)๋ก ์ ๊ณตํด์ฃผ๋ ๋๊ตฌ์ด๋ค. ์ผ์ข ์ ์๋ฐ ์ฑ ๋ธ๋๋ฐ์ค.
์ฌ์ฉ๋ฒ์ ์ฑ ์์์ ์ JVM ๋ช ๋ น์ด๋ก ํ์ฑํ ํ๊ฑฐ๋, ์์์ ์ธ๊ธํ jcmd ๋ช ๋ น์ ์ด์ฉํด์ ์คํ๋์ค ํ์ฑํ ํ ์ ์๋ค.
JDK8์์๋ ์คํ์ ์ธ ๊ธฐ๋ฅ์ด๋ผ ๋ณ๋์ ํ์ฑํ๊ฐ ํ์ํ์ผ๋, JDK11 ๋ถํฐ๋ ์ฑ ๋์์ ๊ธฐ๋ณธ์ผ๋ก ์ฌ์ฉ๋๋ค.
JFR์ JDK ๋ด๋ถ์์ ์ด๋ฒคํธ์ ๋ํ ์ ๋ณด๋ค์ flight.jfr ์ ์ ์ฅํ๋ค. ์ด ์ ๋ณด๋ค์ JMC๋ก ์ฝ์ด๋ค์ฌ ์๊ฐํ ํ ์ ์๋ค.
์ด๋ ๊ฒ JFR์ ํ์ฑํํ๋ฉด, JVM์ ๋ชจ๋ํฐ๋งํ์ฌ ์ป์ ์ด๋ฒคํธ ์ ๋ณด๋ค์ .jfr์ ์ ์ฅํ๋ค. ๋ค๋ง ์ฑ์์ ์ด๋ฐ ๋์คํฌ I/O์์ ์ ์ถ๊ฐ์ ์ธ ๋น์ฉ์ด ๋ง์ด ๋ ๋ค. ๊ทธ๋์ JFR์ ๋ฐ์ดํฐ ๋ธ๋ก์ ๋์คํฌ์ ์ ์ฅํ๊ธฐ ์ ๋ค์ํ ๋ฒํผ๋ฅผ ์ฌ์ฉํ์ฌ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ค.
์ด ๊ณผ์ ์ ๋ณต์กํ๊ธฐ์ ์์ธํ๊ฒ ์ ํ์๋ ์์ง๋ง, JMC์์๋ ์์ฑ๋ ์๊ฐ ์์๋๋ก ์๊ฐํ ํด์ฃผ์ง๋ง JFR ์ถ๋ ฅํ์ผ์ด ์ค์ ์์ฑ๋ ์๊ฐ์ ๋ค๋ฅผ ์ ์๋ค๋ ์ ๋ง ์ ์ํด์ ์ฌ์ฉํ์.
# JFR ์์
๊ณ ์์ ์ผ๋ก OutOfMemoryError๋ฅผ ๋ฐ์์ํค๋ ํ๋ก๊ทธ๋จ์ ๋ง๋ค์ด๋ณด์.
์์ธ๊ฐ ๋ฐ์ํ ๋ ๊น์ง ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ , ์์ธ๊ฐ ๋ฐ์ํ๋ค๋ฉด ํ๋ก๊ทธ๋จ์ 1์ด๋์ ๋ฉ์ถ๋๋ก ์ฝ๋๋ฅผ ์์ฑํ์๋ค.
public static void main(String[] args) {
List<Object> items = new ArrayList<>(1);
try {
while (true){
items.add(new Object());
}
} catch (OutOfMemoryError e){
System.out.println(e.getMessage());
}
assert items.size() > 0;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
}
@ JFR ํ์ฑํ
๊ทธ๋ฆฌ๊ณ ํด๋น ํด๋์ค๋ฅผ ์ปดํ์ผ ํ ํ, JVM์ ์๋์ ๊ฐ์ ๋ช ๋ น์ด๋ก ์คํ์ํค๋ฉด ๋๋ค.
โก JDK 8์ ๊ฒฝ์ฐ, JFR์ ๋ผ์ด์ ์ค๋ฅผ ํ์ํ๋ ๊ธฐ๋ฅ์ด๊ธฐ์ UnlockCommercialFeatures ์ต์ ์ ๋ฃ์ด์ฃผ์ด์ผ ํ๋ค.
โก ์๋ ๋ช ๋ น์ ๊ฒฝ์ฐ myrecording.jfr ํ์ผ์ 200์ด๋์ MyClassName ์ ์ ๋ณด๋ฅผ ์ ์ฅํ๋๋ก ์ค์ ํ์๋ค.
java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder
-XX:StartFlightRecording=duration=200s,filename=myrecording.jfr
-cp ./out/ com.jiwon.package.MyClassName
๋ง์ฝ ํ๋ก๊ทธ๋จ์ด ์ด๋ฏธ ์คํ์ค์ด๋ผ๋ฉด, ์๋์ ๊ฐ์ด jcmd๋ฅผ ์ด์ฉํ์ฌ JFR์ ์์ํ ์ ์๋ค.
๋ค๋ง UnlockCommercialFeatures ์ต์ ์ด ๋ฐ๋์ ์ฑ ์์์์ ํฌํจ๋์ด์์ด์ผ JVM์์ JFR์ ํ์ฑํ ํ ์ ์๋ค.
jcmd 5368 JFR.start duration=200s filename=myrecording.jfr
@ .jfr ๋ถ์
JMX๋ฅผ ์ด์ฉํ๋ ๋๊ตฌ์ธ JMC (Java Mission Control)์ ์ด์ฉํ๋ฉด ์๋์ ๊ฐ์ด .jfr ํ์ผ์ ํ์ธํ ์ ์๋ค.
๋ฉ์ธํ๋ฉด์์๋ CPU์ ์ฌ์ฉ๋์ ๋ํ ์ ๋ณด๋ฅผ ๋ณด์ฌ์ฃผ๋๋ฐ, while ๋ฃจํ๋ก ์ธํด ์ฌ์ฉ๋์ด ์๋นํ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๊ทธ ์ธ ์ผ์ชฝ ํญ์์ Memory, Code, Thread๋ฑ ๋ค์ํ ์ ๋ณด๋ฅผ ํ์ธํ ์ ์๋ค. ๋ฉ์๋ ํธ์ถ ํต๊ณ๋ฑ๋ ํ์ธ ๊ฐ๋ฅํ๋ค.
๋ถ์ ๊ฒฐ๊ณผ, java.util.ArrayList.grow(int) ๋ฉ์๋๊ฐ 17๋ฒ์ด๋ ํธ์ถ๋์ด CPU์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์๋นํ ๋ง์ด ์ก์๋จน๋ ๊ฒ์ ์ ์ ์๋ค. ์ด ์์ ์์๋ ๋จ์ํ ์ ๋ณด๋ฐ์ ์ป์ ๊ฒ ์์์ง๋ง, ์ค์ ๋ก๋ ์๋์ ๊ฐ์ ์ ์ฉํ ์ ๋ณด๋ฅผ ์ป์ ์ ์๋ค.
- ์์ฑ๋ ๊ฐ์ฒด์ ๋ํ ํต๊ณ, GC์ ์ํด ์์ฑ ๋ฐ ๋ฑ๋ก, ์๋ฉธ๋๋ ์์
- ์ค๋ ๋๊ฐ ์ ๊ฒผ๊ฑฐ๋ ๋ค์ ํ์ฑํ ๋ ์๊ธฐ์ ๋ํ ์์ธํ ๋ณด๊ณ ์
- ์ ํ๋ฆฌ์ผ์ด์ ์ด ์คํ์ค์ธ I/O ์์ ๋ค
'๐๊ธฐ๋ณธ ์ง์ > Java ๊ธฐ๋ณธ์ง์' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์ค๋ธ์ ํธ ๋์์ธ ์คํ์ผ #1 ๊ฐ์ฒด์งํฅ, ์๋น์ค ์์ฑํ๊ธฐ (0) | 2021.10.04 |
---|---|
๊ฐ๋น์ง ์ปฌ๋ ํฐ์ TLAB (0) | 2021.09.22 |
์๋ฐ์ ์์ฑ์ ๋์๋ฐฉ์ (+Innerํด๋์ค) (0) | 2021.08.22 |
๋ฐ์ดํธ์ฝ๋ ์กฐ์(๋ฆฌํ๋ ์ , ๋ค์ด๋๋ฏน ํ๋ก์, ์ ๋ ธํ ์ด์ ํ๋ก์ธ์) (0) | 2021.08.17 |
JVM์ ์๋ฆฌ์ ๋ฐ์ดํธ์ฝ๋ (0) | 2021.08.16 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
JiwonDev
JiwonDev