Java8 コーディングベストプラクティス and NetBeansのメモリログから...

483 views

Published on

JJUG CCC 2017 Springの資料です

Published in: Software
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
483
On SlideShare
0
From Embeds
0
Number of Embeds
123
Actions
Shares
0
Downloads
1
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Java8 コーディングベストプラクティス and NetBeansのメモリログから...

  1. 1. NetBeansのメモリ使用ログから 機械学習で きしだが働いてるかどうか判定する 2017/5/20 LINE Fukuoka きしだ なおき Java8 コーディング ベストプラクティス &
  2. 2. 自己紹介 ● きしだ なおき ● LINE FukuokaでJavaを書いてます
  3. 3. 今日のはなし ● Java8コーディングベストプラクティス ● NetBeansのメモリログから、機械学習で きしだが働いてるかどうか判定する
  4. 4. ところで、なぜ2本立て? ● Call for Papersに2本出した – Java8コーディング〜(30分) – NetBeansのメモリログから〜(50分)
  5. 5. ところで、なぜ2本立て? ● 両方とおった – お、おう
  6. 6. ところで、なぜ2本立て? ● やっぱり1枠で。50分で – でもJava8のほうが人気タカカッタヨ
  7. 7. ところで、なぜ2本立て? ● あ、45分で – Java8中心にやろう – 先にNetBeansのメモリログから〜をやります
  8. 8. NetBeansのメモリログから 機械学習で きしだが働いてるかどうか 判定する
  9. 9. メモリログ ● ヒープの使用量 あそんでる あそんでるなんかしてる
  10. 10. どう取るか ● JMXという仕組みがある ● InfluxDBにつっこむ ● Grafanaで見る
  11. 11. 構成 なんかプログラム JMX https://github.com/kishida/analyzenb/blob/master/src/main/java/kis/analyzenb/JmxInflux.java
  12. 12. 問題 ● Javaのメモリ使用グラフは、GCがあるので波形データに なる – 単純な閾値では判定できない ● 時系列データの判定は単純な機械学習では難しい
  13. 13. 周波数解析する ● 時間ごとの周波数解析を行う ● フーリエ変換 – データ全体の周波数解析を行う – 三角関数で畳み込む ● 窓関数付きフーリエ変換 – 時間ごとにデータを切り出して フーリエ変換を行う ● ウェーブレット変換 – 短く切り出した三角関数などを使って 畳み込む – 処理も簡単 http://d.hatena.ne.jp/nowokay/20161227 ※畳み込み zip(data1, data2) .map((a, b) -> a * b) .sum()
  14. 14. 離散ウェーブレット変換 ● 隣同士を引いて2で割る – 移動差分 – ハイパスフィルタになる(高い周波数だけ残す) – ウェーブレット値 ● 隣同士を足して2で割る – 移動平均 – ローパスフィルタになる(低い周波数だけ残す) – より低い周波数の解析に使う
  15. 15. ウェーブレット変換でできること ● 情報圧縮 – JPEG2000で使われている ● 電子すかし(ステガノグラフィ) – 人間にはわからないように情報を画像などに埋め込む 上位1/4の周波数成分だけ残してウェーブレット逆変換 http://d.hatena.ne.jp/nowokay/20161229
  16. 16. ウェーブレット変換の適用 ● 512データ(8分30秒)をウェーブレット変換 する ● 512 = 2^9 – 9+1=10周波数のデータができる
  17. 17. 機械学習 ● ディープじゃないラーニング ● 3層のニューラルネットワーク ● 各時間ごとに10個のデータ ● 出力は1つ ● 10 -> 5 -> 1 https://github.com/kishida/analyzenb/blob/master/src/main/java/kis/analyzenb/BackPropergation.java
  18. 18. 教師データはどうする? ● 機械学習では、学習用に、入力値とその入力が どう判定されるべきかという教師データが必要
  19. 19. Thread count ● NetBeansが使っているスレッド数 ● NetBeansを触ってないときはスレッド数が32 ● 教師データに使える
  20. 20. 機械学習とか不要では? ● ロマンです
  21. 21. 結果 ● なんとなく判定できている https://github.com/kishida/analyzenb/blob/master/src/main/java/kis/analyzenb/UsageData.java
  22. 22. 他の手法 ● ニューラルネットではなくSVMで識別する ● RNN(循環ニューラルネットワーク)を使う – 学習がうまくいけばウェーブレット変換と 同じ計算になりうる ● 近傍法など障害検知手法を使う – 多値分類はできない – 学習が不要
  23. 23. まとめ ● 時系列データのウェーブレット変換からなんと なく状態を把握することができた ● 常に働いていれば判定など不要
  24. 24. Java8コーディング ベストプラクティス
  25. 25. 話すこと ● Java8でのコーディングで気をつけることを まとめる ● APIの使い方自体はちゃんと把握しておく
  26. 26. Agenda ● 一般的なこと ● Java 8 ● Java 7以前
  27. 27. 一般的なこと ● Immutability ● 論理式をうまく使う ● 大事なものはインデントを浅く ● オーバーロード
  28. 28. Immutability ● 変数への再代入を避ける ● ImmutableList/Mapを使う
  29. 29. 変数への再代入を避ける String message = "default"; if (hoge) { message = "ほげ"; } String message; if (hoge) { message = "ほげ"; } else { message = "default"; }
  30. 30. 変数への再代入を避ける String message; if (hoge) { message = "ほげ"; } else { message = "default"; } ● 再代入がないほうが最適化がかかりやすい ● 代入を忘れた場合にコンパイルエラーになる
  31. 31. ImmutableList/Mapを使う ● Guavaのライブラリ ● Collectors::toListの代わりに ImmutableList::toImmutableList ● ofが便利 – Java 9が待てない人のために
  32. 32. toImmutableList strs.stream() .map(String::toUpperCase) .collect(ImmutableList.toImmutableList()); ● 値が変更されないことを明示する ● 並列実行で不具合が起きない
  33. 33. ofが便利 ● 固定値のListやMapを作る場合 ● Java 9ではList.of/Map.ofが入る Map<String, String> countries = ImmutableMap.of( "JP", "日本", "US", "アメリカ");
  34. 34. 論理式をうまく使う ● ド・モルガンの法則を活用する ● 片方が定数リテラルを返す条件演算子は 論理演算にできる ● ifでbooleanを反転させない
  35. 35. ド・モルガンの法則を使う if (!(str != null && !str.isEmpty())) { ... } if (str == null || str.isEmpty()) { ... }
  36. 36. リテラルを返す式を論理演算に ● 片方がbooleanリテラルの条件式は論理演算に boolean f = str == null ? true : str.isEmpty(); boolean f = str == null || str.isEmpty();
  37. 37. 大事なものはインデントを浅く ● 早期リターン if (hoge) { Foo foo = bar.getSomething(); return foo.getImportantValue(); } return null; if (!hoge) { return null; } Foo foo = bar.getSomething(); return foo.getImportantValue();
  38. 38. ifでbooleanを反転させない ● booleanリテラルを返すとき、条件が なりたったときにはtrue、elseでは falseを返すようにする。 if (hoge) { someProc(); return false; } else { anotherProc(); return true; } if (!hoge) { anotherProc(); return true; } else { someProc(); return false; }
  39. 39. オーバーロード ● 引数省略のために使う ● 処理を集約させる ● 引数が多いものを前に void foo(int count, String message) { IntStream.range(0, count) .mapToObj(i -> i + ":" + message) .forEach(System.out::println); } void foo(String message) { foo(1, message); } void foo() { foo(null); }
  40. 40. 処理が違うならメソッド名を変える List<Product> find(Author a); List<Product> find(Category c); List<Product> findByAuthor(Author a); List<Product> findByCategory(Category c);
  41. 41. Java 8 ● Stream ● Optional ● FunctionalInterface ● ラムダ ● Date and Time
  42. 42. Stream ● forEachのifをfilterにできる ● forEachでの値変換はmapにできる ● forEachの入れ子はflatMapにできる ● count() > 0は使わない
  43. 43. forEachのifをfilterにできる strs.forEach(s -> { if (s.length() >= 5) { return; } String upper = s.toUpperCase(); Stream.of(upper.split(",")) .forEach(Syastem.out::println); }); strs.stream() .filter(s -> s.length() < 5) .forEach(s -> { String upper = s.toUpperCase(); Stream.of(upper.split(",")) .forEach(System.out::println); });
  44. 44. forEachでの値変換はmapにできる strs.stream() .filter(s -> s.length() < 5) .forEach(s -> { String upper = s.toUpperCase(); Stream.of(upper.split(",")) .forEach(System.out::println); }); strs.stream() .filter(s -> s.length() < 5) .map(s -> s.toUpperCase()) .forEach(upper -> { Stream.of(upper.split(",")) .forEach(System.out::println); });
  45. 45. forEachの入れ子はflatMapにできる strs.stream() .filter(s -> s.length() < 5) .map(s -> s.toUpperCase()) .forEach(upper -> { Stream.of(upper.split(",")) .forEach(System.out::println); }); strs.stream() .filter(s -> s.length() < 5) .map(s -> s.toUpperCase()) .flatMap(upper -> Stream.of(upper.split(","))) .forEach(System.out::println);
  46. 46. 同じオブジェクトには同じ変数 strs.stream() .filter(s -> s.length() < 5) .map(s -> s.toUpperCase()) .flatMap(upper -> Stream.of(upper.split(","))) .forEach(System.out::println); strs.stream() .filter(a -> a.length() < 5) .map(b -> b.toUpperCase()) .flatMap(upper -> Stream.of(upper.split(","))) .forEach(System.out::println);
  47. 47. Streamで書くメリット ● 行われる操作が限定される ● 変数の行く先を追わなくてよい ● 結果、読みやすくなる strs.stream() .filter(s -> s.length() < 5) .map(s -> s.toUpperCase()) .flatMap(upper -> Stream.of(upper.split(","))) .forEach(System.out::println);
  48. 48. count() > 0は使わない strs.stream().filter(s -> s.length() > 5).count() > 0 strs.stream().anyMatch(s -> s.length() > 5)
  49. 49. 例外処理はあきらめる ● 例外と関数型プログラミングは相性が悪い ● あきらめてtry〜catchを書く ● streamをあきらめてfor文を使う
  50. 50. Optional ● ifPresentのifをfilterにできる ● ifPresentでの値変換はmapにできる ● ifPresentの入れ子はflatMapにできる ● 引数には使わない ● orElseThrowにException::newを書かない
  51. 51. ifPresentのifをfilterに strOpt.ifPresent(s -> { if (s.length() >= 5) { return; } String upper = s.toUpperCase(); findSome(upper) // return Optional .ifPresent(Syastem.out::println); }); strOpt.filter(s -> s.length() >= 5) .ifPresent(s -> { String upper = s.toUpperCase(); findSome(upper) // return Optional .ifPresent(System.out::println); });
  52. 52. ifPresentでの値変換をmapに strOpt.filter(s -> s.length() >= 5) .ifPresent(s -> { String upper = s.toUpperCase(); findSome(upper) // return Optional .ifPresent(System.out::println); }); strOpt.filter(s -> s.length() >= 5) .map(s -> s.toUpperCase()) .ifPresent(upper -> { findSome(upper) // return Optional .ifPresent(System.out::println); });
  53. 53. ifPresentの入れ子をflatMapに strOpt.filter(s -> s.length() >= 5) .map(s -> s.toUpperCase()) .ifPresent(upper -> { findSome(upper) // return Optional .ifPresent(System.out::println); }); strOpt.filter(s -> s.length() >= 5) .map(s -> s.toUpperCase()) .flatMap(upper -> findSome(upper)) .ifPresent(System.out::println);
  54. 54. どこかで見た。 ● Optional.ifPresentはStream.forEachと同じ – Optionalを要素1つまでのStreamと考える
  55. 55. 引数にOptionalを使わない ● メソッド定義者がnullに対応する ● フレームワークの入り口は除く
  56. 56. OrElseThrowにException::newを 渡さない ● なにかのキーから値を取得しようと思って失敗 している。 ● 例外の原因を究明するためにはキーの値が必要 ● 例外メッセージにキーの値を含める必要がある
  57. 57. FunctionalInterface ● 実装したクラスを作らない ● ラムダを割り当てた変数を作らない String twice(String s) { return s + s; } strs.stream() .map(Util::twice) .forEach(System.out::println); Function<String, String> twice = s -> s + s; strs.stream() .map(twice) .forEach(System.out::println);
  58. 58. ラムダ ● むやみにAtomicInteger/Longを使わない AtomicInteger count = new AtomicInteger(0); strs.stream() .filter(s -> s.length() < 5) .forEach(s -> count.incrementAndGet()); int[] count = {0}; strs.stream() .filter(s -> s.length() < 5) .forEach(s -> ++count[0]);
  59. 59. Date and Time ● 自分で時間を計算しない long TIME_OUT_MILLIS = 3 * 60 * 60 * 1000; long TIME_OUT_MILLIS = Duration.ofHours(3).toMillis();
  60. 60. Java7以前 ● LinkedListは使わない ● IOExeptionをRuntimeExceptionでラップ しない
  61. 61. LinkedListは使わない ● ほとんどの場合、ArrayListのほうが速い ● LinkedList.remove/insertの実装がひどい – 値をたどってから操作を行う – 途中への値挿入・削除が効率いいという連結リスト のメリットを持っていない
  62. 62. IOExceptionを RuntimeExceptionでラップしない ● UncheckedIOExceptionを使う try { readFile(); } catch (IOException ex) { throw new RuntimeException(); } try { readFile(); } catch (IOException ex) { throw new UncheckedIOException(); }
  63. 63. まとめ ● 細かいことを積み重ねるとケアレスミスが減っ ていきます。 ● 1ヶ月くらいで慣れて自然にバグが減ります(個 人の感想です)

×