effective java 輪読会 第2章 項目5,6,7

28
Effective Java 輪輪輪 輪 1 輪輪 5 7 2013/11/27 輪輪輪 輪輪

Upload: appresso-engineering-team

Post on 31-May-2015

678 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: Effective java 輪読会 第2章 項目5,6,7

Effective Java 輪読会 第 1 回(項目 5 ~ 7 )

2013/11/27

開発部 野口

Page 2: Effective java 輪読会 第2章 項目5,6,7

項目 5 不必要なオブジェクトの生成を避ける

Page 3: Effective java 輪読会 第2章 項目5,6,7

不変( immutable )オブジェクト

• 例 1 : String クラス{ String s1 = new String("stringette"); // この行の実行時にオブジェクトが生成される

...

{ String s2 = new String("stringette"); // この行でも!}

Page 4: Effective java 輪読会 第2章 項目5,6,7

不変( immutable )オブジェクト

• 例 1 : String クラスについては、こう書けばよい

{ String s = "stringette“; // 同一仮想マシンで動作し、同じ文字列リテラルを持っている他のコードでも同じオブジェクトを再利用する}

Page 5: Effective java 輪読会 第2章 項目5,6,7

不変( immutable )オブジェクト

• 例 2 : Boolean クラス

{ String booleanString = “true”; Boolean b = Boolean(booleanString); // この行の実行時にオブジェクトが生成される}

Page 6: Effective java 輪読会 第2章 項目5,6,7

不変( immutable )オブジェクト

• 例 2 : Boolean クラスについては、こう書けばよい

{ String booleanString = “true”; Boolean b = Boolean.valueOf(booleanString); // 新たなオブジェクトを生成しない}

Page 7: Effective java 輪読会 第2章 項目5,6,7

可変( mutable )オブジェクト• 例 : pp.21 isBabyBoomer() メソッド– boomStart と boomEnd は何度呼ばれても変化すること

がない!にもかかわらず、呼ばれるたびに• Calendar• TimeZone• Date * 2

 のインスタンスを生成してしまう。

Page 8: Effective java 輪読会 第2章 項目5,6,7

可変( mutable )オブジェクト• 例 : pp.21 isBabyBoomer() メソッドでは、こう書けばよい– static 初期化子を使って、定数 BOOM_START と

BOOM_END を一度だけ初期化する

Page 9: Effective java 輪読会 第2章 項目5,6,7

可変( mutable )オブジェクト• 注意 : 遅延初期化について– でも isBabyBoomer() が呼ばれなかった

時、 BOOM_START と BOOM_END の初期化が無駄になるので、遅延初期化を行うのがベストなのでは……?

 →実装が複雑になり、パフォーマンスにも大した改善が見られないことが多いので、おすすめしない。

★ パフォーマンス改善の第一原則 : 測れ★ 時期尚早な最適化は諸悪の根源

Page 10: Effective java 輪読会 第2章 項目5,6,7

Adapter パターン• 例 : Map#KeySet– Map#KeySet() は Map のキーの集合のビューを返すに

すぎない。– 何度呼ばれても、その都度インスタンスを生成しない。

(同じインスタンスを返す)– 疑問 : 「 Adapter パターン」と書いた(書いてある)

が、「 Map 」は「 Set 」への「 Adapter 」なのか?という点については、疑問。「 Set 」が「ビュー」ということなら納得できるが……。

 → KeySet が Set へのアダプター

Page 11: Effective java 輪読会 第2章 項目5,6,7

オートボクシング• 例 : Long

※Joshua Block のマシン(スペック不明)では、 43.2 秒もかかる! ( Long を使用しなければ、 6.8 秒)

public static void main(String[] args) { Long sum = 0L; for (long i = 0; i <= Integer.MAX_VALUE; i++) { sum += 1; // ここでオートボクシングが行われる! } System.out.println(sum);}

Page 12: Effective java 輪読会 第2章 項目5,6,7

注意• 小さなオブジェクトを避けろというわけではな

い– JVM が賢く振る舞ってくれたりする– 明瞭性、簡潔性、能力(?)を向上するために必要

なら、それはよいこと

Page 13: Effective java 輪読会 第2章 項目5,6,7

オブジェクトプール• 「オブジェクトプール」はよくない– コードが複雑になる– そもそも、 JVM のガーベッジコレクタに任せる方が

性能が高い可能性が高い– たとえば、相当重く、コネクション数が限られてた

りする DB コネクション等は例外(プールする意味がある)

Page 14: Effective java 輪読会 第2章 項目5,6,7

防御的コピー• See also : 項目 39 防御的コピー(必要な場合は、

オブジェクトを生成する)

Page 15: Effective java 輪読会 第2章 項目5,6,7

項目 6 廃れたオブジェクト参照を取り除く

Page 16: Effective java 輪読会 第2章 項目5,6,7

メモリリーク• 例 : pp.24 の Stack クラス– ここでリークする : return elements[--size];– デクリメントする前の「 size - 1 」の要素には外部か

らアクセスする手段がなくなるが、配列 elements はその要素オブジェクトへの参照を保持している<廃れた参照>

– 廃れた参照が参照しているオブジェクト……が参照しているオブジェクト……と言った具合に、多量のオブジェクトがリークするおそれもある

Page 17: Effective java 輪読会 第2章 項目5,6,7

メモリリーク• 例 : pp.24 の Stack クラスへの解決策– elements[size] = null; // 廃れた参照を取り除く– もし間違ってその要素を参照した場合

に、 NullPointerExeption が発生することによって問題が早期にわかるというメリットもある

– 注意:普通は、ローカル変数の適切な定義によって参照が自然にスコープからはずれるようにすべき

Page 18: Effective java 輪読会 第2章 項目5,6,7

メモリリーク• リークしやすいとき– クラスが独自のメモリを管理している時– キャッシュ– リスナーやコールバック

Page 19: Effective java 輪読会 第2章 項目5,6,7

メモリリーク• キャッシュの問題への対処– WeakHashMap で実装する– Timer や ScheduledThreadPoolExecutor を用いる– 新たなエントリーを追加する際の副作用として行う

Page 20: Effective java 輪読会 第2章 項目5,6,7

メモリリーク• リスナーやコールバックへの対処– 弱い参照を用いる(例: WeakHashMap )

Page 21: Effective java 輪読会 第2章 項目5,6,7

項目 7 ファイナライザを避ける

Page 22: Effective java 輪読会 第2章 項目5,6,7

ファイナライザはC++ のデストラクタとは違う

• デストラクタ: C++ におけるリソース回収の一般的な手段– RAII ( Resource Aquisition Is Initialization )– Java でデストラクタにあたるのは、ファイナライザで

はなく try-finally ブロック

Page 23: Effective java 輪読会 第2章 項目5,6,7

ファイナライザの欠点• 即座にファイナライザが実行される保証がない– ガーベッジコレクションの機能であり、 JVM の実装

に依存する– 低い優先順位で動作する「ファイナライザスレッ

ド」のために、ファイナライザを提供したことでオブジェクトの回収が通常よりも遅れることさえある

– ファイナライザが実行される保証自体ない• DB に対する永続性のあるロックをファイナライザ(だけ)

で解放すると、……( Coding Horror )– System.gc 、 System.runFinalization 、 System.runFinalize

rsOnExit 、  Runtime.runFinalizersOnExit 等々もいけてない

Page 24: Effective java 輪読会 第2章 項目5,6,7

ファイナライザの欠点• ファイナライザでスローされた例外がキャッチ

されなかった場合、ファイナライザが単に終了する

• 深刻に遅い(実測値:ファイナライザがない場合の約 430倍)

Page 25: Effective java 輪読会 第2章 項目5,6,7

かわりに明示的終了メソッドを提供しよう

• 例 : InputStream 、 OutputStream 、 java.sql.Connection の close メソッド– try-finally で終了を保証する

Page 26: Effective java 輪読会 第2章 項目5,6,7

ファイナライザが有効なとき• 安全ネット– その際は、エラーログを出すべき

• 重要な資源を保持していないネイティブピアの回収– 即座に開放すべき資源を保持しているなら、明示的終了メソッドを持つべき

Page 27: Effective java 輪読会 第2章 項目5,6,7

「ファイナライザ連鎖」は自動的に実行されない

• サブクラスで finalize() をオーバーライドしたら、スーパークラスのファイナライザを明示的に呼ぶ必要がある!– ここでも try-finally を使用する

• ファイナライザガーディアンによって、 finalize() の呼び出しを保証できる– 疑問:これならもはや finalize() というメソッド名である

必要すらないのでは?– 疑問:というか、 finalize() というメソッド名にすると必

ず 2 回(以上)呼ばれてしまったりするのでは? →そもそも、ファイナライザガーディアンを設定するときにはエンクロージングクラスには finalize() を設けない!

Page 28: Effective java 輪読会 第2章 項目5,6,7

まとめ• 安全ネットあるいは重要ではないネイティブ資

源を解放させること以外で、ファイナライザを使用してはいけない

• super.finalize を呼び出す• 安全ネットとしての使用時には、エラーログを出す

• ファイナライザガーディアンの使用を検討する