Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

10,985 views

Published on

JJUG CCC 2017 Springの資料です

Published in: Software
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

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ヶ月くらいで慣れて自然にバグが減ります(個 人の感想です)

×