狠狠撸

狠狠撸Share a Scribd company logo
2018年12月15日
数村 憲治
GCを発生させない
JVMとコーディングスタイル
Copyright 2018 FUJITSU LIMITED0
JJUG CCC 2018 Fall
.setName(“数村憲治”)
.setDirectorOf(“Eclipse Foundation Board”)
.setMemberOf(“Jakarta EE Committee”)
.setMemberOf(“JCP Executive Committee”)
.setTwitter(“@kkzr”)
.setResponsibleFor(“Launcher”)
.build();
スピーカー
Copyright 2018 FUJITSU LIMITED
Speaker me = SpeakerBuilder
1
アジェンダ
Copyright 2018 FUJITSU LIMITED
GC問題
アプリケーションによる対処
コンパイラによる対処
サマリ
2
アジェンダ
Copyright 2018 FUJITSU LIMITED
GC問題
アプリケーションによる対処
コンパイラによる対処
サマリ
3
何が問題か
Copyright 2018 FUJITSU LIMITED
健康診断シンドローム
アプリケーションの実行が止まる
アプリケーションに割り当てられるCPU時間が減る
実はたいした問題ではない
4
スループット
レスポンス
なぜ問題か
Copyright 2018 FUJITSU LIMITED
(Cでは、malloc/free回数に依存)
チューニングがたいへん
最適解をみつけるのが難しい
環境が変わるとやりなおし
見積もりができない
GC時間は残存オブジェクトに依存
5
問題の対処方法
Copyright 2018 FUJITSU LIMITED
あきらめる SLAの妥協
Javaを使わない
オブジェクトの
生成数?量を減らす
アプリケーションで頑張る
GCで頑張る
コンパイラで頑張る
優秀なGCを作る
オブジェクトの
生成を最適化
6
デモ
Copyright 2018 FUJITSU LIMITED7
Node.js
$ node –trace-gc node.js
$ graalvm-ce-1.0.0-rc10/bin/node --jvm.Xmx16m --jvm.Xms16m --jvm.verbose:gc node.js
アジェンダ
Copyright 2018 FUJITSU LIMITED
GC問題
アプリケーションによる対処
コンパイラによる対処
サマリ
8
アプリケーションで頑張る
Copyright 2018 FUJITSU LIMITED
オブジェクトプール (キャッシュ)
スレッドプール
コネクションプール
小ネタ
ArrayList 拡張
String.intern()
Boxing/Unboxing
9
Integer AutoBoxing
Copyright 2018 FUJITSU LIMITED10
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
キャッシュの範囲はプロパティで指定可能
java.lang.Integer.IntegerCache.high
よく使われる範囲はIntegerオブジェクトの使いまわし
Integer.java
プーリングコスト
Copyright 2018 FUJITSU LIMITED
1*(メモリ割当+初期化) + p*GCp
n*(メモリ割当+初期化) + u*GCu
プーリング使用時
プーリング不使用時
n: トランザクション回数
p: プーリング使用時のGC回数
u: プーリング不使用時のGC回数
GCp: プーリング使用時の1回のGC時間
GCu: プーリング不使用時の1回のGC時間
p < u
GCp > GCu
初期化 < n*初期化
メモリ割当は問題にならない
11
一般的な傾向
Edenからのアロケーション
Copyright 2018 FUJITSU LIMITED
o = new Object();
bottom top end
lock(Eden);
top = top + size;
unlock(Eden);
Bump The Pointer
bottom top end
12
size
TLAB (Thread-Local Allocation Buffer)
Copyright 2018 FUJITSU LIMITED
スレッド1スレッド2
Eden領域をスレッドごとに分割して割当て
ロックが不要に
13
lock(Eden);
top = top + size;
unlock(Eden);
Fast Allocation
Copyright 2018 FUJITSU LIMITED
https://www.oracle.com/technetwork/java/javase/memorymanagement-whitepaper-150215.pdf
only requiring around 10 native instructions
Memory Management in the Java
HotSpot Virtual Machine
Bump the Pointer + TLAB で実現
14
デモ
Copyright 2018 FUJITSU LIMITED15
Bump The Pointer
$ java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp -XX:CompileCommand=compileonly,Bump.doIt -XX:-UseTLAB Bump
$ java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp -XX:CompileCommand=compileonly,Bump.doIt Bump
(注)PrintAssemblyを使うには、hsdisが必要。https://github.com/liuzhengyang/hsdis
TLABが向かないケース
Copyright 2018 FUJITSU LIMITED
スレッド N+1 用のTLABが割当てられない
スレッド1スレッド2 スレッドN
???
スレッド数が多く、かつ、オブジェクト生成量が少ない場合
16
デモ
Copyright 2018 FUJITSU LIMITED17
Thread-Local Allocation Buffer
$ java -Xmx32m -Xms32m -verbose:gc TLAB
$ java -Xmx32m -Xms32m -verbose:gc -XX:-UseTLAB TLAB
アジェンダ
Copyright 2018 FUJITSU LIMITED
GC問題
アプリケーションによる対処
コンパイラによる対処
サマリ
18
デモ
Copyright 2018 FUJITSU LIMITED19
メモリアロケーション量とGC頻度
$ java -Xmx32m -Xms32m -verbose:gc EA
Escape Analysis (EA)
Copyright 2018 FUJITSU LIMITED
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/performance-
enhancements-7.html
JDK 6 (HotSpot C2) から採用
JITによる最適化
Scalar Replacement
Stack Allocation
Lock Elision
20
Escapeとは
Copyright 2018 FUJITSU LIMITED
メソッドからの脱出
オブジェクトがアロケートされたメソッドの外で
使用されているかもしれない
オブジェクトはメソッドローカルでない
スレッドからの脱出
オブジェクトがアロケートされたスレッドの外で
使用されているかもしれない
他のスレッドがそのオブジェクトにアクセスするかもしれない
21
Escapeの種類
Copyright 2018 FUJITSU LIMITED
オブジェクトはグローバルに脱出する
オブジェクトは作成したメソッドを脱出しない
オブジェクトは引数経由でメソッドを脱出するが、
スレッドは脱出しない
NoEscape
ArgEscape
GlobalEscape
22
Escapeの例
Copyright 2018 FUJITSU LIMITED
int foo(String name) {
Person person = new Person(name);
return person.id();
}
void foo(String name) {
Person person = new Person(name);
register(person);
}
NoEscape
ArgEscape
23
Escapeの例
Copyright 2018 FUJITSU LIMITED
class bar {
Person person;
void foo(String name) {
person = new Person(name);
}
}
GlobalEscape
24
最適化の組み合わせ
Copyright 2018 FUJITSU LIMITED
Scalar
Replacement
Stack
Allocation
Lock
Elision
No
Escape 〇 〇 〇
Arg
Escape - - 〇
Global
Escape - - -
25
Stack Allocation
Copyright 2018 FUJITSU LIMITED
オブジェクトをヒープではなくスタックにアロケートする
メソッド終了と同時にオブジェクトは解放
HotSpotでは不採用
26
Stack Allocation
Copyright 2018 FUJITSU LIMITED
ヘッダ
32
山田
太郎
SP
メソッド
開始時
new Person()
実行後
メソッド
終了時
残骸
SP
SP
27
void foo {
Person person = new Person(32, “山田太郎”);
???
スタック
Scalar Replacement
Copyright 2018 FUJITSU LIMITED
オブジェクトフィールドへのアクセスを、
スタックまたはレジスターアクセスにする
Stack Allocationとは異なり、
オブジェクトヘッダー等はアロケートされない
28
Scalar Replacement 例
Copyright 2018 FUJITSU LIMITED
int foo(String name, int age) {
Person person = new Person(name, age);
return person.getAge();
}
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
}
29
デモ
Copyright 2018 FUJITSU LIMITED30
Scalar Replacement
$ java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=print,ScalarReplace.doIt -XX:-DoEscapeAnalysis ScalarReplace
$ java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=print,ScalarReplace.doIt -XX:+DoEscapeAnalysis ScalarReplace
$ java -verbose:gc -Xmx32m -Xms32m -XX:CompileCommand=dontinline,ScalarReplace.getPersonId ScalarReplace
Lock Elision
Copyright 2018 FUJITSU LIMITED
スレッドローカルなオブジェクトに対するロックを削除
31
void foo() {
Object lock = new Ojbect();
synchronized (lock) {
//doSomething
};
}
アジェンダ
Copyright 2018 FUJITSU LIMITED
GC問題
アプリケーションによる対処
コンパイラによる対処
サマリ
32
サマリ
Copyright 2018 FUJITSU LIMITED
GCの問題と対処
JVMの種類やインライン状況によっては、
GCが発生したり、しなかったり
33
まずは、見やすいコードを
GCは日々改善されている
Javaのアロケーションコストは低い
EAによるメモリ最適化もあり
Copyright 2018 FUJITSU LIMITED
Q/A
34
骋颁を発生させない闯痴惭とコーディングスタイル

More Related Content

骋颁を発生させない闯痴惭とコーディングスタイル

  • 2. .setName(“数村憲治”) .setDirectorOf(“Eclipse Foundation Board”) .setMemberOf(“Jakarta EE Committee”) .setMemberOf(“JCP Executive Committee”) .setTwitter(“@kkzr”) .setResponsibleFor(“Launcher”) .build(); スピーカー Copyright 2018 FUJITSU LIMITED Speaker me = SpeakerBuilder 1
  • 3. アジェンダ Copyright 2018 FUJITSU LIMITED GC問題 アプリケーションによる対処 コンパイラによる対処 サマリ 2
  • 4. アジェンダ Copyright 2018 FUJITSU LIMITED GC問題 アプリケーションによる対処 コンパイラによる対処 サマリ 3
  • 5. 何が問題か Copyright 2018 FUJITSU LIMITED 健康診断シンドローム アプリケーションの実行が止まる アプリケーションに割り当てられるCPU時間が減る 実はたいした問題ではない 4 スループット レスポンス
  • 6. なぜ問題か Copyright 2018 FUJITSU LIMITED (Cでは、malloc/free回数に依存) チューニングがたいへん 最適解をみつけるのが難しい 環境が変わるとやりなおし 見積もりができない GC時間は残存オブジェクトに依存 5
  • 7. 問題の対処方法 Copyright 2018 FUJITSU LIMITED あきらめる SLAの妥協 Javaを使わない オブジェクトの 生成数?量を減らす アプリケーションで頑張る GCで頑張る コンパイラで頑張る 優秀なGCを作る オブジェクトの 生成を最適化 6
  • 8. デモ Copyright 2018 FUJITSU LIMITED7 Node.js $ node –trace-gc node.js $ graalvm-ce-1.0.0-rc10/bin/node --jvm.Xmx16m --jvm.Xms16m --jvm.verbose:gc node.js
  • 9. アジェンダ Copyright 2018 FUJITSU LIMITED GC問題 アプリケーションによる対処 コンパイラによる対処 サマリ 8
  • 10. アプリケーションで頑張る Copyright 2018 FUJITSU LIMITED オブジェクトプール (キャッシュ) スレッドプール コネクションプール 小ネタ ArrayList 拡張 String.intern() Boxing/Unboxing 9
  • 11. Integer AutoBoxing Copyright 2018 FUJITSU LIMITED10 public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } キャッシュの範囲はプロパティで指定可能 java.lang.Integer.IntegerCache.high よく使われる範囲はIntegerオブジェクトの使いまわし Integer.java
  • 12. プーリングコスト Copyright 2018 FUJITSU LIMITED 1*(メモリ割当+初期化) + p*GCp n*(メモリ割当+初期化) + u*GCu プーリング使用時 プーリング不使用時 n: トランザクション回数 p: プーリング使用時のGC回数 u: プーリング不使用時のGC回数 GCp: プーリング使用時の1回のGC時間 GCu: プーリング不使用時の1回のGC時間 p < u GCp > GCu 初期化 < n*初期化 メモリ割当は問題にならない 11 一般的な傾向
  • 13. Edenからのアロケーション Copyright 2018 FUJITSU LIMITED o = new Object(); bottom top end lock(Eden); top = top + size; unlock(Eden); Bump The Pointer bottom top end 12 size
  • 14. TLAB (Thread-Local Allocation Buffer) Copyright 2018 FUJITSU LIMITED スレッド1スレッド2 Eden領域をスレッドごとに分割して割当て ロックが不要に 13 lock(Eden); top = top + size; unlock(Eden);
  • 15. Fast Allocation Copyright 2018 FUJITSU LIMITED https://www.oracle.com/technetwork/java/javase/memorymanagement-whitepaper-150215.pdf only requiring around 10 native instructions Memory Management in the Java HotSpot Virtual Machine Bump the Pointer + TLAB で実現 14
  • 16. デモ Copyright 2018 FUJITSU LIMITED15 Bump The Pointer $ java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp -XX:CompileCommand=compileonly,Bump.doIt -XX:-UseTLAB Bump $ java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp -XX:CompileCommand=compileonly,Bump.doIt Bump (注)PrintAssemblyを使うには、hsdisが必要。https://github.com/liuzhengyang/hsdis
  • 17. TLABが向かないケース Copyright 2018 FUJITSU LIMITED スレッド N+1 用のTLABが割当てられない スレッド1スレッド2 スレッドN ??? スレッド数が多く、かつ、オブジェクト生成量が少ない場合 16
  • 18. デモ Copyright 2018 FUJITSU LIMITED17 Thread-Local Allocation Buffer $ java -Xmx32m -Xms32m -verbose:gc TLAB $ java -Xmx32m -Xms32m -verbose:gc -XX:-UseTLAB TLAB
  • 19. アジェンダ Copyright 2018 FUJITSU LIMITED GC問題 アプリケーションによる対処 コンパイラによる対処 サマリ 18
  • 20. デモ Copyright 2018 FUJITSU LIMITED19 メモリアロケーション量とGC頻度 $ java -Xmx32m -Xms32m -verbose:gc EA
  • 21. Escape Analysis (EA) Copyright 2018 FUJITSU LIMITED https://docs.oracle.com/javase/8/docs/technotes/guides/vm/performance- enhancements-7.html JDK 6 (HotSpot C2) から採用 JITによる最適化 Scalar Replacement Stack Allocation Lock Elision 20
  • 22. Escapeとは Copyright 2018 FUJITSU LIMITED メソッドからの脱出 オブジェクトがアロケートされたメソッドの外で 使用されているかもしれない オブジェクトはメソッドローカルでない スレッドからの脱出 オブジェクトがアロケートされたスレッドの外で 使用されているかもしれない 他のスレッドがそのオブジェクトにアクセスするかもしれない 21
  • 23. Escapeの種類 Copyright 2018 FUJITSU LIMITED オブジェクトはグローバルに脱出する オブジェクトは作成したメソッドを脱出しない オブジェクトは引数経由でメソッドを脱出するが、 スレッドは脱出しない NoEscape ArgEscape GlobalEscape 22
  • 24. Escapeの例 Copyright 2018 FUJITSU LIMITED int foo(String name) { Person person = new Person(name); return person.id(); } void foo(String name) { Person person = new Person(name); register(person); } NoEscape ArgEscape 23
  • 25. Escapeの例 Copyright 2018 FUJITSU LIMITED class bar { Person person; void foo(String name) { person = new Person(name); } } GlobalEscape 24
  • 26. 最適化の組み合わせ Copyright 2018 FUJITSU LIMITED Scalar Replacement Stack Allocation Lock Elision No Escape 〇 〇 〇 Arg Escape - - 〇 Global Escape - - - 25
  • 27. Stack Allocation Copyright 2018 FUJITSU LIMITED オブジェクトをヒープではなくスタックにアロケートする メソッド終了と同時にオブジェクトは解放 HotSpotでは不採用 26
  • 28. Stack Allocation Copyright 2018 FUJITSU LIMITED ヘッダ 32 山田 太郎 SP メソッド 開始時 new Person() 実行後 メソッド 終了時 残骸 SP SP 27 void foo { Person person = new Person(32, “山田太郎”); ??? スタック
  • 29. Scalar Replacement Copyright 2018 FUJITSU LIMITED オブジェクトフィールドへのアクセスを、 スタックまたはレジスターアクセスにする Stack Allocationとは異なり、 オブジェクトヘッダー等はアロケートされない 28
  • 30. Scalar Replacement 例 Copyright 2018 FUJITSU LIMITED int foo(String name, int age) { Person person = new Person(name, age); return person.getAge(); } class Person { String name; int age; Person(String name, int age) { this.name = name; this.age = age; } } 29
  • 31. デモ Copyright 2018 FUJITSU LIMITED30 Scalar Replacement $ java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=print,ScalarReplace.doIt -XX:-DoEscapeAnalysis ScalarReplace $ java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=print,ScalarReplace.doIt -XX:+DoEscapeAnalysis ScalarReplace $ java -verbose:gc -Xmx32m -Xms32m -XX:CompileCommand=dontinline,ScalarReplace.getPersonId ScalarReplace
  • 32. Lock Elision Copyright 2018 FUJITSU LIMITED スレッドローカルなオブジェクトに対するロックを削除 31 void foo() { Object lock = new Ojbect(); synchronized (lock) { //doSomething }; }
  • 33. アジェンダ Copyright 2018 FUJITSU LIMITED GC問題 アプリケーションによる対処 コンパイラによる対処 サマリ 32
  • 34. サマリ Copyright 2018 FUJITSU LIMITED GCの問題と対処 JVMの種類やインライン状況によっては、 GCが発生したり、しなかったり 33 まずは、見やすいコードを GCは日々改善されている Javaのアロケーションコストは低い EAによるメモリ最適化もあり
  • 35. Copyright 2018 FUJITSU LIMITED Q/A 34