[Java/Instrumentation] Instrumentation.getObjectSize()
1. Overview
Instrumentation 의 getObjectSize를 통해 Object 의 Size를 조사하는 방법
Java bytecode에 개입할 수 있는 Instrumentation Class의 getObjectSize method를 통해서 Object의 Size를 조사하는 방법을 소개한다.
2. ObjectSizeAgent
getObjectSize 를 수행하는 ObjectSizeAgent App은 javaagent 로 심어져야 한다.
App은 다음과 같은 구조로 개발된다.
1
2
3
4
5
6
7
$ tree /sw/app/ObjectSizeAgent/
/sw/app/ObjectSizeAgent/
├── compile.sh
├── MANIFEST.MF
├── ObjectSizeAgent.class
├── ObjectSizeAgent.jar
└── ObjectSizeAgent.java
2.1 compile.sh
편의를 위해 compile script를 만들었다.
1
2
javac ObjectSizeAgent.java
jar cvfm ObjectSizeAgent.jar MANIFEST.MF ObjectSizeAgent.class
java 를 compile 하고 ObjectSizeAgent.jar 에 MANIFEST.MF와 class를 packaging 한다.
2.2 MANIFEST.MF
생략가능하다.
1
Premain-Class: ObjectSizeAgent
2.3 ObjectSizeAgent.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.lang.instrument.Instrumentation;
public class ObjectSizeAgent {
private static volatile Instrumentation instrumentation;
public static void premain(String agentArgs, Instrumentation inst) {
instrumentation = inst;
}
public static long getObjectSize(Object object) {
if (instrumentation == null) {
throw new IllegalStateException("Java agent not initialized");
}
return instrumentation.getObjectSize(object);
}
}
3. My App
javaagent로 등록된 ObjectSizeAgent.jar 에서 getObjectSize method를 호출하여 특정 Object의 Size를 알 수 있다.
나에게는 Session을 다루는 Servlet 이 있으며, 여러 Size를 계산할 필요가 있었다.
3.1 SessionServlet.java
Servlet code는 아래와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
@WebServlet("/SessionServlet")
public class SessionServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ArrayList<byte[]> list = new ArrayList<byte[]>();
int addedNum = 500;
int addedByte = 1;
byte[] objectInSession = new byte[addedByte];
for(int i=0; i<addedNum; i++){
list.add(objectInSession);
}
HttpSession session = request.getSession(true);
ArrayList<byte[]> sList = (ArrayList<byte[]>)session.getAttribute("listSession");
if (sList == null){
sList = list;
}
else{
sList.addAll(list);
}
session.setAttribute("listSession", sList);
String log = "";
log += "list.size() = " + list.size() + "\r\n";
log += "sList.size = " + sList.size() + "\r\n";
log += "session.getMaxInactiveInterval() = " + session.getMaxInactiveInterval() + "\r\n";
log += "session.getClass().getName() = " + session.getClass().getName() + "\r\n";
log += "ObjectSizeAgent.getObjectSize(list) = " + ObjectSizeAgent.getObjectSize(list) + "\r\n";
log += "ObjectSizeAgent.getObjectSize(sList) = " + ObjectSizeAgent.getObjectSize(sList) + "\r\n";
log += "ObjectSizeAgent.getObjectSize(session) = " + ObjectSizeAgent.getObjectSize(session) + "\r\n";
System.out.println(log);
final long MEGABYTE = 1024L * 1024L;
long heapSize = Runtime.getRuntime().totalMemory() / MEGABYTE;
long heapMaxSize = Runtime.getRuntime().maxMemory() / MEGABYTE;
long heapFreeSize = Runtime.getRuntime().freeMemory() / MEGABYTE;
log = "";
log += "heapSize (MB) = " + heapSize + "\r\n";
log += "heapMaxSize (MB) = " + heapMaxSize + "\r\n";
log += "heapSize (MB) = " + heapFreeSize + "\r\n";
System.out.println(log);
}
}
ObjectSizeAgent.getObjectSize() method를 통해서 Size를 inspect 하고 있다.
3.2 compile.sh
여기서도 편의를 위해 compile script를 사용하고 있다.
1
2
3
4
5
6
. /sw/weblogic/14c/domains/base_domain/bin/setDomainEnv.sh
CLASSPATH="${CLASSPATH}:/sw/app/ObjectSizeAgent/ObjectSizeAgent.jar"
cd /sw/app/cohSessionApp/WEB-INF
javac src/*.java -d classes/
Weblogic 14c에 배포되는 특성이 있으며, classpath에 Agent jar가 있다.
4. Weblogic
Weblogic에 javaagent를 등록하여 기동한다.
1
-javaagent:/sw/app/ObjectSizeAgent/ObjectSizeAgent.jar
이외 별달리 할 것은 없다.
5. Test
App을 호출하면 Session에 담기려는 Size, Session data 자체의 Size가 return 된다.
호출 예시
1
2
3
4
5
6
7
8
9
10
11
list.size() = 500
sList.size = 1000
session.getMaxInactiveInterval() = 30
session.getClass().getName() = weblogic.servlet.internal.session.CoherenceWebSessionData
ObjectSizeAgent.getObjectSize(list) = 24
ObjectSizeAgent.getObjectSize(sList) = 24
ObjectSizeAgent.getObjectSize(session) = 128
heapSize (MB) = 2967
heapMaxSize (MB) = 2967
heapSize (MB) = 2307
특이사항으로는, 나의 App은 반복 호출 시 더 큰 Object를 Session에 저장하는데
getObjectSize로 확인해도 항상 24bytes 를 유지하는 모습이 관찰된다.
이는 getObjectSize는 Object의 shallow size를 return 하기 때문이라며,,
공식자료는 찾지 못했다.
deep size 확인을 위해서는, JOL(Java Object Layout) 3rd library 를 활용해야 될 것으로 보인다.