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 를 활용해야 될 것으로 보인다.