ITお絵かき修行

3歩歩いても忘れないために

jol(Java Object Layout)を使ったオブジェクトのサイズ取得

jol(Java Object Layout)というOpenJDKに含まれるツールを使用して、オブジェクトのメモリサイズが取得できるらしいので試してみる。


OpenJDK: jol


■環境
Windows7 Home Premium 64bit
JDK 1.8.0_60-b27
Eclipse Luna SR1 (4.4.1)
・jol (0.4) (※jol-cli-0.4-full.jar)


■すること
jolを使用して何ができるのかを調べる。
主目的は、1処理におけるメモリ量の取得とする。
よって割とメイン所(と思われる)の機能のみ追いかける。


■クラス紹介
1.ClassLayoutクラス
jolを使用する上で基本となるクラス。クラス・クラスインスタンスのメモリ情報を取り扱う。

●処理対象特定系

通番 static 戻り値の型 メソッド 引数 メソッド概要
1 ClassLayout parseInstance Object インスタンスのメモリ情報を取得する。
2 ClassLayout parseInstance Object, Layouter インスタンスのメモリ情報を取得する。第二引数のLayouterで読み込み時の動作変更が可能
3 ClassLayout parseClass Object クラスのメモリ情報を取得する。
4 ClassLayout parseClass Object, Layouter クラスのメモリ情報を取得する。第二引数のLayouterで読み込み時の動作変更が可能

●サイズ取得系

通番 static 戻り値の型 メソッド 引数 メソッド概要
5 SortedSet< FieldLayout> fields - インスタンス内に含まれるフィールドのメモリ情報を取得する。
6 int headerSize - インスタンスのヘッダサイズ(int値)を取得する。
7 int instanceSize - インスタンスのサイズ(int値)を取得する。
8 String toPrintable - インスタンスのメモリ情報を整形して詳細情報を出力する。
9 String toString - インスタンスのメモリ情報を整形して出力する。


2.FieldLayoutクラス
フィールドのメモリ情報を取り扱う。

●サイズ取得系

通番 static 戻り値の型 メソッド 引数 メソッド概要
1 String name - フィールド名を取得する。
2 String offset - インスタンスが占めるサイズの先頭からのメモリ量を取得する。
3 int size - フィールドのサイズを取得する。
4 String typeClass - フィールドの型を取得する。
5 String toString - フィールドの詳細を取得する。


3.VMSupportクラス
JVMのメモリに関連する情報を取得する。

通番 static 戻り値の型 メソッド 引数 メソッド概要
1 String vmDetails - JVMのメモリ情報を出力する。


4.GraphLayoutクラス
インスタンス生成からGraphLayout#parseInstanceした時点までにおける、指定したインスタンスに対するメモリの状態を可視化する。

通番 static 戻り値の型 メソッド 引数 メソッド概要
1 GraphLayout parseInstance Object インスタンスのメモリ情報を取得する。
2 - toImage String 引数で指定したファイル名(~.png等)でグラフを生成する。


●HelloWorld的なやつ

package com.hhhhhskw.jol;

import java.util.SortedSet;

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.info.FieldLayout;
import org.openjdk.jol.util.VMSupport;

public class HelloJol {

	private String HELLO_JOL_HOGE = "HOGEEEEEEEEEEEEEEEEE";
	private String HELLO_JOL_FUGA = "FUGAAAAAAAAAAAAAAAAA";


	public static void main(String[] args) {
		HelloJol hj = new HelloJol();
		System.out.println("■JVM情報■");
		System.out.println(VMSupport.vmDetails());

		System.out.println("■クラスインスタンスのメモリ情報■");
		System.out.println(ClassLayout.parseInstance(hj).instanceSize());
		System.out.println(ClassLayout.parseInstance(hj));
		System.out.println(ClassLayout.parseInstance(hj).toPrintable());

		SortedSet<FieldLayout> flSet = ClassLayout.parseInstance(hj).fields();

		flSet.stream().forEachOrdered(fl -> {
			System.out.println("■フィールドのメモリ情報■");
			System.out.println(fl.name());
			System.out.println(fl.size());
			System.out.println(fl.typeClass());
			System.out.println(fl);
		});

		System.out.println(flSet);
	}
}


●実行結果*1

■JVM情報■
WARNING: Unable to attach Serviceability Agent. You can try again with super-user privileges. Use -Djol.tryWithSudo=true to try with sudo.
WARNING: VM details, e.g. object alignment, reference size, compressed references info will be guessed.

Running 64-bit HotSpot VM.
Using compressed oop with 3-bit shift.
Using compressed klass with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

■クラスインスタンスのメモリ情報■
24
HelloJol.HELLO_JOL_HOGE@12(4)
HelloJol.HELLO_JOL_FUGA@16(4)
size = 24

com.hhhhhskw.jol.HelloJol object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                    VALUE
      0     4        (object header)                01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                05 c0 00 f8 (00000101 11000000 00000000 11111000) (-134168571)
     12     4 String HelloJol.HELLO_JOL_HOGE        (object)
     16     4 String HelloJol.HELLO_JOL_FUGA        (object)
     20     4        (loss due to the next object alignment)
Instance size: 24 bytes (estimated, add this JAR via -javaagent: to get accurate result)
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

■フィールドのメモリ情報■
HELLO_JOL_HOGE
4
String
HelloJol.HELLO_JOL_HOGE@12(4)
■フィールドのメモリ情報■
HELLO_JOL_FUGA
4
String
HelloJol.HELLO_JOL_FUGA@16(4)
[HelloJol.HELLO_JOL_HOGE@12(4), HelloJol.HELLO_JOL_FUGA@16(4)]



【感想】
・継承関係にあるクラスのフィールドの中身までさかのぼってサイズ取得してくれるので便利。
・アプリケーション単位で特定の箇所でメモリサイズを取得する場合、インスタンスを適当に決めると、重複してサイズ取得する恐れがあるので、取得対象とするインスタンスは慎重に選ぶべき。
・VMSupport、GraphLayoutクラスを使用したサンプルは下記のソースを参照。
code-tools/jol: 4443d2696dcf /jol-samples/src/main/java/org/openjdk/jol/samples/


【参考】
[Java]オブジェクトの使用メモリを測る - Qiita

*1:「sa-jdi.jar」を入れ、VM引数に「-Djol.tryWithSudo=true」を指定することで警告を消せる可能性がある。警告は無視しても動作する。 ※手元の環境では、左記の対応を行ったうえで管理者権限で実行しても変化しなかった。。実行環境をOpenJDKにした方がよいかも。