それでも環境依存は残っている
                     起きたり起きなかったりする問題のお話

Hiroki Tateno
Senior Principal Engineer
Sustaining Engineering
Oracle Japan
1   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ここで示されている見解は私個人の
ものであり、所属会社の見解を反映
したものではありません
       OracleとJavaは、Oracle Corporation 及びその子会社、関連会社の米国及びその他の国における登録商標です。
       文中の社名、商品名等は各社の商標または登録商標である場合があります。




2   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
 はじめに
Program
Agenda                                                                      反則勝ち or 反則負け
                                                                            ピタゴラスイッチ
                                                                            完全犯罪
                                                                            おわりに


3   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
はじめに




4   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
自己紹介

     氏名:立野広樹
     仕事 : WebLogic Server 開発
     ペンシルパズル好き ニコリ好き nikoli.com会員
     2000年 日本オラクル入社
     Java関連 トラブルシューティングひとすじ13年目
     Sustaining Engineering =問題解析&障害修正
     全世界に400+人以上
     解析、解析、解析、そしてまた解析。 時々修正。
5   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
理想の障害調査&修正


     目的が明確
     問題も明確
     調査手法は自明
     情報は十分
     ゴールの判定も明確
     良ゲー


6   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
現実の障害調査&修正

 目的が不明
 問題が成立してない
 調査手法が謎
 情報不足
 もうゴールしてもいいよね…
 無理ゲー

 無理ゲーを解けるゲームにする!

7   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
そんな私の愛読書は…




8   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
常識(=思い込み)が通用しなくなる瞬間


     i を2で割った余りは0か1しかないのか?
     i + 1 > i は常に正しいか?
     12 + 2l がなぜ33にならないのか?


     知らない!! 興味ある!! という方は、今すぐJava
       PuzzlersにGO!!

9   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
転ばぬ先の… ?


 「罠・落とし穴・コーナーケース」
 罠はある 一つ一つは小さいけど
 何度でも蘇る 但し別の場所に
 Java Puzzlersがおしえてくれたこと
 コーナーケースがどこにあるかという「知識」ではなく……
 コーナーケースがどこにありうるかという「感覚」

10   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
It’s just a joke, but…

プログラムが動かないときの、プログラマの言い訳

第5位 それ、動かないとホントに困るの? (えっうん)
第4位 そのOSには対応してないんだよね (たまにある…)
第3位 使い方が悪い (これも実はよくある…)
第2位 プログラムが壊れたとき、君はどこにいたんだ?(責任転嫁!)
第1位 私のマシンではちゃんと動くよ
                                                                            (from http://hp.vector.co.jp/authors/VA000092/jokes/ )
11   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
Let’s start !



                                                                   環境依存
                                                                  コーナーケース
                                                                  探求の旅へ…
12   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
反則勝ち or 反則負け




13   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
要件


      ロードされてるクラスが何なのか知りたい
      -verbose:class
      プログラムから知りたい…
      java.lang.ClassLoaderをチェック!
      getLoadedClassNames() きっとあるよね….
      存在しない….

14   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
たぶんこれが正解だけど…


       ClassLoaderを自作して
       MBeanも自作する
                                                                            _人人人人人人人_
                                                                            > 面倒!!! <
                                                                             ̄^Y^Y^Y^Y^Y^Y ̄

15   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
OpenJDK 7のClassLoaderを見てみると…

public abstract class ClassLoader {
  // The classes loaded by this class loader.
// The only purpose of this table
// is to keep the classes from being GC'ed until
// the loader is GC'ed.
private final Vector<Class<?>> classes = new Vector<>();

いまどき Vector なんて…                                                             ( ´,_ゝ`)プッ
privateだけどリフレクション使えば取り出せるぞ
 16   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
リフレクションを使ってみよう
     Field classesField =
       ClassLoader.class.getDeclaredField("classes");
     classesField.setAccessible(true);
     Vector<Class> classes =
       (Vector<Class>)classesField.get(
         ClassLoader.getSystemClassLoader());
     for (Class classObj: classes)
         System.out.println("loaded:"+classObj);


17   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
反則勝ち


     * JRockit
     > java TestLoadedClasses
     loaded:class oracle.jrockit.jfr.VMJFR
     loaded:class TestLoadedClasses    *     +                                               巛ヽ
                                                                                              〒 ! +   。     +   。     *
                                           +                                                。 | |
     ...                                 *                                                +   / / イヤッッホォォォオオォオウ!
                                                                                      ∧_∧ / /

     * HotSpot                                                                       (´∀` / / +
                                                                                     ,-
                                                                                    / ュヘ
                                                                                              f
                                                                                               |*
                                                                                                    。

                                                                                                    +   。
                                                                                                          +   。

                                                                                                              +
                                                                                                                *

                                                                                                                。 +
                                                                                                                      。


                                                                                  〈_} )       |
     > java TestLoadedClasses                                                           /     !+  。     +   +    *
                                                                                      ./ ,ヘ |
     loaded:class TestLoadedClasses                                          ガタン ||| j / | | |||
                                                                            ――――――――――――

18   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
反則負け


     *IBM JDK (on AIX / Linux)
       Exception in thread "main"
       java.lang.NoSuchFieldException: classes
                                                                             *      +      巛ヽ
                                                                                           〒 !    +      。 +                 。         *
                                                                                        | 。 | |
                                                                                ゴツン |★     / /   +      。           +           。 +

           Sun JDK由来のクラスライブラリ                                                  ___|_∧ / /
                                                                                     (´∀` / / +      。           。           *         。
                                                                                     ,-     f
            以外で動かない                                                                 / ュヘ
                                                                                   〈_} )
                                                                                             |*
                                                                                             |
                                                                                                     +       。           +       。 +

                                                                                        /    !+    。         + +                 *

           互換性はTCKの範囲                                                                ./ ,ヘ |
                                                                             ガタン ||| j / | | |||
                                                                            ――――――――――――

19   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
リフレクションは両刃の剣

 APIが用意されてないデータにアクセスできる
 どうしても欲しい場合がある
 黒魔術
 別の実装だと動かなくなるかもしれない
 バージョンアップしたら動かなくなるかもしれない
 分かって使うしかない
 分かっていてもハマるときはハマる
20   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピタゴラスイッチ




21   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
どっちが新しい?
     File file1 = new File("file1");
     file1.createNewFile();
     File file2 = new File("file2");
     file2.createNewFile();
     System.out.println(
       file2.lastModified() > file1.lastModified() ?
       “file1 is old" : "file1 is NOT older than file2");




22   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
異なる実行結果


     * Windows
     > java TestLastModified
     file1 is old.


     * Linux etc...                                                         何故?
     $ java TestLastModified
     file1 is NOT older than file2.


23   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ファイルシステムの時刻精度


      “通常、秒単位に丸められたファイル変更時刻をサ
        ポートしますが、中にはもっと高い精度をサポートす
        るものもあります” (from java.io.File)
          Windows : NTFSは100ns単位で保持
          Linux : ext3は秒単位
          lastModifiedの比較は環境によって動作が違う
          ….で、何が問題なわけ?
24   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
殆どのケースでは問題ではない

      どういう時に比較したいか?
      よくあるパターン
                  ソースファイルを生成→コンパイルしてバイナリを生成
                  ソースファイルが更新→再コンパイルしてバイナリも更新
      ソースファイルがバイナリよりも新しかったら問題になる
      そういうことはない
      ….で、何が問題なわけ?

25   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ヒヤリ・ハット

      唐突に話題は転換する
      ハインリッヒの法則「重大事故の陰に29倍の軽度事故と、300
       倍のニアミスが存在する」(from wikipedia「ヒヤリ・ハット」)
      1件の重大バグの裏に29倍の軽微な不具合と300倍の勘違
       いがある?
      「一つ一つの仕様の勘違いは大きな問題でなくて
        も、たまたま複数の動作が重なったとき、不具合
        になる、ことがあるかも」
26   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピタゴラスイッチ(1)

      よくある要件
      「スクリプト」を受け取って、そこから「Javaソース」を作って、
       最終的にJavaコンパイラで「クラスファイル」を生成したい。
       ……大量に。
                                                                             Java
                                                                               Java       Class
                                                 Script                         Java
                                                                            Source         Class
                                                                                            Class
                                                  Script                     Source        File
                                                   Script                     FileJava
                                                                               Source
                                                                                File
                                                                                            File
                                                                                               Class
                                                                                              File
                                                     Script                      Source
                                                                                 File           File
                                                                                   File

27   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピタゴラスイッチ(2)
     public void generate(List<Task> tasks) {
        foreach (Task task : tasks) {
            File source = generateSourceCode(task);
            compileCode(source, sourceEncoding);
        }
     }




28   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピタゴラスイッチ(3)


      大量すぎるので中間ファイルはメモリに格納しよう
      メモリは正義!!

                                                                             byte[]         Class
                            Script                                            byte[]
                                                                             Java            Class
                             Script                                             byte[]
                                                                              Java           File
                                                                                               Class
                              Script                                             byte[]
                                                                            Source
                                                                                Java          File
                                                                                                 Class
                                Script                                       Sourcebyte[]
                                                                                 Java           File
                                                                                                  Class
                                 Script                                        Source
                                                                                    Java          File
                                                                                Source             File
                                                                                  Source

29   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピタゴラスイッチ(4)
     public void generate(List<Task> tasks) {
        foreach (Task task : tasks) {
            byte[] source = generateSourceCode(task);
            writeSource(source)
            compileCode(source, sourceEncoding);
        }
     }




30   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピタゴラスイッチ(5)


      1つ1つ処理するのは非効率だからまとめよう!


                                       scripts                              sources   classes




31   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピタゴラスイッチ(6)
     public void generate(List<Task> tasks) {
       foreach (Task task : tasks) {
         sourceList.add(generateSourceCode(task));
       }
       compleSources(sourceList, sourceEncoding);
       writeSources(sourceList);
     }




32   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピタゴラスイッチ(7)


      まとめすぎてOutOfMemoryErrorになった……
      適当なサイズでタスク分割しよう

                                scripts                                     sources        classes
                                 scripts                                     sources        classes
                                    scripts
                                     scripts                                   sources
                                                                                sources        classes
                                                                                                classes
                                      scripts                                    sources         classes




33   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピタゴラスイッチ(8)
     public void generate2(List<Task> tasks) {
       List<List<Task>> divided =
         divideTasks(tasks, JOB_SIZE);
       for (List<Task> dividedTasks: divided) {
          generate(dividedTasks);
       }
     }




34   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピダゴラスイッチ(9)


 分割したコンパイルタスク間でソースファイルに依存関係があるぞ
 バイナリはファイルに書き出してあるし
 タスクをまたぐ依存関係は、書き出したファイルを使おう
 ソースよりバイナリのほうが古いと困るな
 再コンパイルの仕組みを入れておこう




35   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピタゴラスイッチ(10)
     public void checkRecompile(File binary) {
       File source = getSourceFile(binary);
       if (source.lastModified() > binary.lastModified())
                                   doRecompile(source); // 時刻比較してる
     }




36   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピタゴラスイッチ(11)

      後日……
      『あの、ものすごく大規模なプロジェクトのタスクを日本語で書
        いた時、Windowsだけちゃんと動かないんですケド……』
     問題発生!!

      スクリプトを日本語以外で書いた場合は動く
      日本語で書いた場合もWindows以外なら動く

37   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピタゴラスイッチ(12)

 エラー「Javaソース上でダブルクォーテーションが閉じてないよ」
 典型的な「非MS932のソースをMS932としてコンパイルした」問題
  (昔はたまに遭遇したが最近ほとんど見ない…)
 -Dfile.encodingで明示的に文字コードを設定したら動く
 でも、設定しなくても99%動く
 コード上ではencodingを明示的に指定している
 ああ、それなのに、それなのに

38   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピタゴラスイッチ(13)

 原因1
        歴史的経緯により、日本語Windows環境は、javacのデフォルト
     ソースコードエンコーディングはMS932。別の文字コードで書か
     れてる場合は、明示的にエンコーディングを指定しないとエラー。
 原因2
    ソースをオンメモリに格納する最適化をした時に、バイナリを先
     に書き出して、それからソースを書きだしていた。(バグといえば
     バグかもしれないが、ファイルに書き出したソースはコンパイル
     とは関係ないはずだった)
 39   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピタゴラスイッチ(14)

 原因3
       巨大なタスクを分割処理する変更を加えた時に、オンメモリのク
     ラスファイルだけではなく、ファイルシステム上のファイルが参
     照されるようになっていた。
 原因4
    ソースとバイナリの日付を比較して、ソースが古い場合はソー
     スが自動的に再コンパイルされるようにした。が、ここではソー
     スの文字エンコードを明示的に指定できなかった(ファイルしか
     見えないので当然ではある)
 40   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピタゴラスイッチ(15)

 帰結
             原因2+原因3+原因4+Windowsのファイル日付精度が
              異なる現象、の複合により、Javaファイルの再コンパイル
              が走るようになってしまった。
             原因1によりJavaファイルの文字コードとプラットフォーム
              デフォルトの文字コードが異なっていた。以上により、コン
              パイルエラーが発生した。
             Windowsでしか起きない為、テストもすり抜けていた。

41   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
ピタゴラスイッチ(15)

 対処
       どんな時も、ソースを書き出してからバイナリを書きだす
 教訓
       大山鳴動しても底にいるのはねずみ一匹くらいかも
       一匹じゃなくてもっといるかも
       小さな違いも、複数組み合わさると、予想しない連鎖をする
        (ピタゴラスイッチ的)
       それが環境依存の振る舞いだと、より複雑に……
 42   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
完全犯罪




43   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
よくある処理


      ディレクトリを作る
      その下にファイルを作る
      何かする
      ファイルを消す
      ディレクトリを消す


      →書いてみる

44   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
処理のひな型
     File dir = new File("temp");
     dir.mkdir();
     File file = new File(dir, "file");
     file.createNewFile();
     doSomething(file);
     if (!file.delete()){
       throw new IOException("failed to delete:"+file); }
     if (!dir.delete()){
       throw new IOException("failed to delete:"+dir); }


45   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
doSomething (バグ入り)
     private static void doSomething(File file)
     throws IOException {
         FileOutputStream os =
            new FileOutputStream(file);
         os.write("test test test¥n".getBytes());
         os.flush();
         // os.close();
     }


46   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
結果(1)

     $ java TestD
     (no error!)




47   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
結果(2)

     > java TestD
     Exception in thread "main"
     java.io.IOException: failed to delete:temp¥file
             at TestD.main(TestD.java:15)




48   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
結果(3)

     $ java TestD
     Exception in thread "main"
     java.io.IOException: failed to delete:temp
             at TestD.main(TestD.java:18)

     $ ls –al temp
     (empty!!)



49   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
結果まとめ


                                                                 環境
             結果1                                                 Linux : 正常動作
             結果2                                                 Windows : ファイル削除に失敗
             結果3                                                 Linux + NFS : ディレクトリ削除に失敗

      結果が全部違う…
      しかも、結果3 に至っては、空ディレクトリなのになぜ
          かディレクトリの削除に失敗している……
50   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
Linux と Windowsの動作の違い


      UNIXは、オープンしているファイルを削除できる
      というか、“delete on last close” という定型コード
      削除したファイルはクローズするまで存在が保証されている


      Windowsのファイルロックは悲観ロック しかも自動
      ファイルをオープンしていたら削除等が出来なくなる
      UNIXと同じようには書けない

51   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
NFSはもっと恐ろしい


      UNIXは、ファイルを削除してもファイルにアクセスできる
      NFSプロトコルでは、「ファイル名」がないとファイルにアクセス
       できない
      すなわち 「オープンされてるファイルは削除できない」
      だけどWindowsと違って「悲観ロックではない」「削除は失敗で
       きない」
      “Silly Rename” (see also: http://nfs.sourceforge.net/ )

52   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
Silly Rename


      NFS上のファイルをオープン
      オープンしたままファイルを削除すると
                  ファイルは削除されない
                  temp -> .nfsXXXXに自動的に名前が変更
      NFSクライアントプロセス終了時
      名前変更されたファイルが自動的に削除される
                  .nfsXXXX -> 削除

53   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
発生していた現象

      NFS上にファイル作成
      NFS上でオープン中のファイルをdelete()
      NFSがファイルを.nfsXXXXにリネーム
      ディレクトリ削除時、ディレクトリ下にファイル.nfsXXXXが存在
      ディレクトリ削除失敗(問題発生)
      IOExceptionでプログラム終了
      プロセス終了時にNFS側で.nfsXXXX削除
        (証拠隠滅完了!暗黙的!全自動!)
54   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
後からはわからない


      再現すればそれほど難しい問題ではない
      deleteに失敗する所にブレークポイントを設定
      しかし他の環境で起きた場合は?
      痕跡は残らないので調査が困難
      close忘れという単純なバグでも起きる
      JDK7にしよう! try-with-resources を使おう!


55   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
doSomething (バグフリー)
     private static void doSomething(File file)
     throws IOException {
       try (FileOutputStream os =
                      new FileOutputStream(file)) {
                        os.write("test test test¥n".getBytes());
                               os.flush();
                    }
     }


56   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
One more thing…


      しかし、この問題が本当に恐ろしいのは、バグでなく
        ても起きる点である。




57   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
doSomething (mmap ver)
     private static void doSomething(File file)
     throws IOException {
       try (RandomAccessFile ras =
         new RandomAccessFile(file,"rw")) {
         FileChannel fc = ras.getChannel();
         MappedByteBuffer mbb =
      fc.map(FileChannel.MapMode.READ_WRITE, 0, 4096);
         mbb.put("test test test¥n".getBytes());
     }}


58   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
原因と結果

      クローズ忘れていた時と同じ
      unmapされてないfileは…
      JavaのFileChannelにはmap()はあるけどunmap()がない
      unmap()はどこから呼ばれる?
      unmap()はいつ呼ばれる?
      Reference Handler Thread上で発見



59   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
unmap時のcall stack (java + native)

     #0  0x0000003dc22d0d20 in munmap () from
         /lib64/libc.so.6
     #1 0x00002aaab6870477 in
         Java_sun_nio_ch_FileChannelImpl_unmap0 ()
     [2] sun.nio.ch.FileChannelImpl$Unmapper.run
         (FileChannelImpl.java:746)
     [3] sun.misc.Cleaner.clean (Cleaner.java:142)
     [4] java.lang.ref.Reference$ReferenceHanlder.run
         (Reference.java:141)
60   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
sun.nio.ch.FileChannelImpl
     public MappedByteBuffer map(MapMode mode, long
      position, long size) throws IOException {
       ....
       Unmapper um = new Unmapper(addr, mapSize, isize,
      mfd);
       ....
       return Util.newMappedByteBufferR(
                 isize, addr + pagePosition, mfd, um);



61   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
Direct-X-Buffer.java.templete
     // For memory-mapped buffers -- invoked by
     // FileChannelImpl via reflection
     protected Direct$Type$Buffer$RW$(int cap, long addr,
      FileDescriptor fd, Runnable unmapper) {
     #if[rw]
             super(-1, 0, cap, cap, fd);
             address = addr;
             cleaner = Cleaner.create(this, unmapper);



62   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
sun.misc.Cleaner
* General-purpose phantom-reference-based cleaners.
 * <p> Cleaners are a lightweight and more robust alternative to finalization.
 * They are lightweight because they are not created by the VM and thus do not


                             長々といろいろ書いてあるけど要は
 * require a JNI upcall to be created, and because their cleanup code is
 * invoked directly by the reference-handler thread rather than by the
 * finalizer thread.                   They are more robust because they use phantom references,
                             クラスライブラリの中からだけつかえる
 * the weakest type of reference object, thereby avoiding the nasty ordering
 * problems inherent to finalization.

                             ファントムリファレンスベースのFinalizerもどき
 * <p> A cleaner tracks a referent object and encapsulates a thunk of arbitrary
 * cleanup code.                Some time after the GC detects that a cleaner's referent has

                             でもFinalizerの代わりにはならない
 * become phantom-reachable, the reference-handler thread will run the cleaner.
 * Cleaners may also be invoked directly; they are thread safe and ensure that


                             単純で素直なクリーンアップの時だけ使える
 * they run their thunks at most once.
 * <p> Cleaners are not a replacement for finalization.                     They should be used


                                                                              →unmap()はGCされたら呼ばれる
 * only when the cleanup code is extremely simple and straightforward.
                            
 * Nontrivial cleaners are inadvisable since they risk blocking the
 * reference-handler thread and delaying further cleanup and finalization.




63   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
mapがunmapされるとき


      MappedBufferがGCされる
      ReferenceHandlerスレッドが動き出す
      Cleanerに登録されていたUnmapperが呼び出される
      munmapシステムコールが呼ばれる




64   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
邪道しかない


      動かす方法はあるが……
                                                                 _人人人人人人人_
                                                                 > System.gc(); <
                                                                  ̄^Y^Y^Y^Y^Y^Y ̄
      邪道!実装依存!
      邪道!実行時依存!
      邪道!タイミング依存!
65   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
蛇足


      実は、本当に起きた現象では、ここでは言い尽くせない理由
        のせいで、テコでもファイルディスクリプタが消えず、結局Silly
        Renameのせいでファイルが消えない問題は解決しなかった
      でも…
                                           プログラムが動かないときの、プログラマの言い訳
                                           第5位 それ、動かないとホントに困るの? (えっうん)


      ユーザは正常に動作させたいだけであって、ディレクトリを消
        したいわけではない(場合もある)
66   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
おわりに




67   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
まとめ


      手元で再現しないバグはある
      様々な異なる実装があっても仕様を満たしていればJava
      同じインタフェースでも返り値の精度が違うことがある
      同じインタフェースでも動作の意味が違うことがある
      明確なバグがなくても環境によって動かないことがある
      実装の抽象化は大事!でも破綻も付き物!
      どこに罠があるかという感覚を養いたい

68   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
Q&Aコーナー




                                                                            ( ´_ゝ`)フーン
69   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
70   Copyright © 2013, Oracle and/or its affiliates. All rights reserved.

それでも環境依存は残っている~起きたり起きなかったりする問題のお話~

  • 1.
    それでも環境依存は残っている 起きたり起きなかったりする問題のお話 Hiroki Tateno Senior Principal Engineer Sustaining Engineering Oracle Japan 1 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 2.
    ここで示されている見解は私個人の ものであり、所属会社の見解を反映 したものではありません OracleとJavaは、Oracle Corporation 及びその子会社、関連会社の米国及びその他の国における登録商標です。 文中の社名、商品名等は各社の商標または登録商標である場合があります。 2 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 3.
     はじめに Program Agenda  反則勝ち or 反則負け  ピタゴラスイッチ  完全犯罪  おわりに 3 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 4.
    はじめに 4 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 5.
    自己紹介  氏名:立野広樹  仕事 : WebLogic Server 開発  ペンシルパズル好き ニコリ好き nikoli.com会員  2000年 日本オラクル入社  Java関連 トラブルシューティングひとすじ13年目  Sustaining Engineering =問題解析&障害修正  全世界に400+人以上  解析、解析、解析、そしてまた解析。 時々修正。 5 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 6.
    理想の障害調査&修正  目的が明確  問題も明確  調査手法は自明  情報は十分  ゴールの判定も明確  良ゲー 6 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 7.
    現実の障害調査&修正  目的が不明  問題が成立してない 調査手法が謎  情報不足  もうゴールしてもいいよね…  無理ゲー  無理ゲーを解けるゲームにする! 7 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 8.
    そんな私の愛読書は… 8 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 9.
    常識(=思い込み)が通用しなくなる瞬間  i を2で割った余りは0か1しかないのか?  i + 1 > i は常に正しいか?  12 + 2l がなぜ33にならないのか?  知らない!! 興味ある!! という方は、今すぐJava PuzzlersにGO!! 9 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 10.
    転ばぬ先の… ?  「罠・落とし穴・コーナーケース」 罠はある 一つ一つは小さいけど  何度でも蘇る 但し別の場所に  Java Puzzlersがおしえてくれたこと  コーナーケースがどこにあるかという「知識」ではなく……  コーナーケースがどこにありうるかという「感覚」 10 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 11.
    It’s just ajoke, but… プログラムが動かないときの、プログラマの言い訳 第5位 それ、動かないとホントに困るの? (えっうん) 第4位 そのOSには対応してないんだよね (たまにある…) 第3位 使い方が悪い (これも実はよくある…) 第2位 プログラムが壊れたとき、君はどこにいたんだ?(責任転嫁!) 第1位 私のマシンではちゃんと動くよ (from http://hp.vector.co.jp/authors/VA000092/jokes/ ) 11 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 12.
    Let’s start ! 環境依存 コーナーケース 探求の旅へ… 12 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 13.
    反則勝ち or 反則負け 13 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 14.
    要件  ロードされてるクラスが何なのか知りたい  -verbose:class  プログラムから知りたい…  java.lang.ClassLoaderをチェック!  getLoadedClassNames() きっとあるよね….  存在しない…. 14 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 15.
    たぶんこれが正解だけど… ClassLoaderを自作して MBeanも自作する _人人人人人人人_ > 面倒!!! <  ̄^Y^Y^Y^Y^Y^Y ̄ 15 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 16.
    OpenJDK 7のClassLoaderを見てみると… public abstractclass ClassLoader { // The classes loaded by this class loader. // The only purpose of this table // is to keep the classes from being GC'ed until // the loader is GC'ed. private final Vector<Class<?>> classes = new Vector<>(); いまどき Vector なんて… ( ´,_ゝ`)プッ privateだけどリフレクション使えば取り出せるぞ 16 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 17.
    リフレクションを使ってみよう Field classesField = ClassLoader.class.getDeclaredField("classes"); classesField.setAccessible(true); Vector<Class> classes = (Vector<Class>)classesField.get( ClassLoader.getSystemClassLoader()); for (Class classObj: classes) System.out.println("loaded:"+classObj); 17 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 18.
    反則勝ち * JRockit > java TestLoadedClasses loaded:class oracle.jrockit.jfr.VMJFR loaded:class TestLoadedClasses * + 巛ヽ 〒 ! + 。 + 。 * + 。 | | ... * + / / イヤッッホォォォオオォオウ! ∧_∧ / / * HotSpot (´∀` / / + ,- / ュヘ f |* 。 + 。 + 。 + * 。 + 。 〈_} ) | > java TestLoadedClasses / !+ 。 + + * ./ ,ヘ | loaded:class TestLoadedClasses ガタン ||| j / | | ||| ―――――――――――― 18 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 19.
    反則負け *IBM JDK (on AIX / Linux) Exception in thread "main" java.lang.NoSuchFieldException: classes * + 巛ヽ 〒 ! + 。 + 。 * | 。 | | ゴツン |★ / / + 。 + 。 +  Sun JDK由来のクラスライブラリ ___|_∧ / / (´∀` / / + 。 。 * 。 ,- f 以外で動かない / ュヘ 〈_} ) |* | + 。 + 。 + / !+ 。 + + *  互換性はTCKの範囲 ./ ,ヘ | ガタン ||| j / | | ||| ―――――――――――― 19 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 20.
    リフレクションは両刃の剣  APIが用意されてないデータにアクセスできる  どうしても欲しい場合がある 黒魔術  別の実装だと動かなくなるかもしれない  バージョンアップしたら動かなくなるかもしれない  分かって使うしかない  分かっていてもハマるときはハマる 20 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 21.
    ピタゴラスイッチ 21 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 22.
    どっちが新しい? File file1 = new File("file1"); file1.createNewFile(); File file2 = new File("file2"); file2.createNewFile(); System.out.println( file2.lastModified() > file1.lastModified() ? “file1 is old" : "file1 is NOT older than file2"); 22 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 23.
    異なる実行結果 * Windows > java TestLastModified file1 is old. * Linux etc... 何故? $ java TestLastModified file1 is NOT older than file2. 23 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 24.
    ファイルシステムの時刻精度  “通常、秒単位に丸められたファイル変更時刻をサ ポートしますが、中にはもっと高い精度をサポートす るものもあります” (from java.io.File)  Windows : NTFSは100ns単位で保持  Linux : ext3は秒単位  lastModifiedの比較は環境によって動作が違う  ….で、何が問題なわけ? 24 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 25.
    殆どのケースでは問題ではない  どういう時に比較したいか?  よくあるパターン  ソースファイルを生成→コンパイルしてバイナリを生成  ソースファイルが更新→再コンパイルしてバイナリも更新  ソースファイルがバイナリよりも新しかったら問題になる  そういうことはない  ….で、何が問題なわけ? 25 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 26.
    ヒヤリ・ハット  唐突に話題は転換する  ハインリッヒの法則「重大事故の陰に29倍の軽度事故と、300 倍のニアミスが存在する」(from wikipedia「ヒヤリ・ハット」)  1件の重大バグの裏に29倍の軽微な不具合と300倍の勘違 いがある?  「一つ一つの仕様の勘違いは大きな問題でなくて も、たまたま複数の動作が重なったとき、不具合 になる、ことがあるかも」 26 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 27.
    ピタゴラスイッチ(1)  よくある要件  「スクリプト」を受け取って、そこから「Javaソース」を作って、 最終的にJavaコンパイラで「クラスファイル」を生成したい。 ……大量に。 Java Java Class Script Java Source Class Class Script Source File Script FileJava Source File File Class File Script Source File File File 27 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 28.
    ピタゴラスイッチ(2) public void generate(List<Task> tasks) { foreach (Task task : tasks) { File source = generateSourceCode(task); compileCode(source, sourceEncoding); } } 28 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 29.
    ピタゴラスイッチ(3)  大量すぎるので中間ファイルはメモリに格納しよう  メモリは正義!! byte[] Class Script byte[] Java Class Script byte[] Java File Class Script byte[] Source Java File Class Script Sourcebyte[] Java File Class Script Source Java File Source File Source 29 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 30.
    ピタゴラスイッチ(4) public void generate(List<Task> tasks) { foreach (Task task : tasks) { byte[] source = generateSourceCode(task); writeSource(source) compileCode(source, sourceEncoding); } } 30 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 31.
    ピタゴラスイッチ(5)  1つ1つ処理するのは非効率だからまとめよう! scripts sources classes 31 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 32.
    ピタゴラスイッチ(6) public void generate(List<Task> tasks) { foreach (Task task : tasks) { sourceList.add(generateSourceCode(task)); } compleSources(sourceList, sourceEncoding); writeSources(sourceList); } 32 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 33.
    ピタゴラスイッチ(7)  まとめすぎてOutOfMemoryErrorになった……  適当なサイズでタスク分割しよう scripts sources classes scripts sources classes scripts scripts sources sources classes classes scripts sources classes 33 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 34.
    ピタゴラスイッチ(8) public void generate2(List<Task> tasks) { List<List<Task>> divided = divideTasks(tasks, JOB_SIZE); for (List<Task> dividedTasks: divided) { generate(dividedTasks); } } 34 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 35.
    ピダゴラスイッチ(9)  分割したコンパイルタスク間でソースファイルに依存関係があるぞ  バイナリはファイルに書き出してあるし タスクをまたぐ依存関係は、書き出したファイルを使おう  ソースよりバイナリのほうが古いと困るな  再コンパイルの仕組みを入れておこう 35 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 36.
    ピタゴラスイッチ(10) public void checkRecompile(File binary) { File source = getSourceFile(binary); if (source.lastModified() > binary.lastModified()) doRecompile(source); // 時刻比較してる } 36 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 37.
    ピタゴラスイッチ(11)  後日……  『あの、ものすごく大規模なプロジェクトのタスクを日本語で書 いた時、Windowsだけちゃんと動かないんですケド……』 問題発生!!  スクリプトを日本語以外で書いた場合は動く  日本語で書いた場合もWindows以外なら動く 37 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 38.
    ピタゴラスイッチ(12)  エラー「Javaソース上でダブルクォーテーションが閉じてないよ」  典型的な「非MS932のソースをMS932としてコンパイルした」問題 (昔はたまに遭遇したが最近ほとんど見ない…)  -Dfile.encodingで明示的に文字コードを設定したら動く  でも、設定しなくても99%動く  コード上ではencodingを明示的に指定している  ああ、それなのに、それなのに 38 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 39.
    ピタゴラスイッチ(13)  原因1  歴史的経緯により、日本語Windows環境は、javacのデフォルト ソースコードエンコーディングはMS932。別の文字コードで書か れてる場合は、明示的にエンコーディングを指定しないとエラー。  原因2  ソースをオンメモリに格納する最適化をした時に、バイナリを先 に書き出して、それからソースを書きだしていた。(バグといえば バグかもしれないが、ファイルに書き出したソースはコンパイル とは関係ないはずだった) 39 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 40.
    ピタゴラスイッチ(14)  原因3  巨大なタスクを分割処理する変更を加えた時に、オンメモリのク ラスファイルだけではなく、ファイルシステム上のファイルが参 照されるようになっていた。  原因4  ソースとバイナリの日付を比較して、ソースが古い場合はソー スが自動的に再コンパイルされるようにした。が、ここではソー スの文字エンコードを明示的に指定できなかった(ファイルしか 見えないので当然ではある) 40 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 41.
    ピタゴラスイッチ(15)  帰結  原因2+原因3+原因4+Windowsのファイル日付精度が 異なる現象、の複合により、Javaファイルの再コンパイル が走るようになってしまった。  原因1によりJavaファイルの文字コードとプラットフォーム デフォルトの文字コードが異なっていた。以上により、コン パイルエラーが発生した。  Windowsでしか起きない為、テストもすり抜けていた。 41 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 42.
    ピタゴラスイッチ(15)  対処  どんな時も、ソースを書き出してからバイナリを書きだす  教訓  大山鳴動しても底にいるのはねずみ一匹くらいかも  一匹じゃなくてもっといるかも  小さな違いも、複数組み合わさると、予想しない連鎖をする (ピタゴラスイッチ的)  それが環境依存の振る舞いだと、より複雑に…… 42 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 43.
    完全犯罪 43 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 44.
    よくある処理  ディレクトリを作る  その下にファイルを作る  何かする  ファイルを消す  ディレクトリを消す  →書いてみる 44 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 45.
    処理のひな型 File dir = new File("temp"); dir.mkdir(); File file = new File(dir, "file"); file.createNewFile(); doSomething(file); if (!file.delete()){ throw new IOException("failed to delete:"+file); } if (!dir.delete()){ throw new IOException("failed to delete:"+dir); } 45 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 46.
    doSomething (バグ入り) private static void doSomething(File file) throws IOException { FileOutputStream os = new FileOutputStream(file); os.write("test test test¥n".getBytes()); os.flush(); // os.close(); } 46 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 47.
    結果(1) $ java TestD (no error!) 47 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 48.
    結果(2) > java TestD Exception in thread "main" java.io.IOException: failed to delete:temp¥file at TestD.main(TestD.java:15) 48 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 49.
    結果(3) $ java TestD Exception in thread "main" java.io.IOException: failed to delete:temp at TestD.main(TestD.java:18) $ ls –al temp (empty!!) 49 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 50.
    結果まとめ 環境 結果1 Linux : 正常動作 結果2 Windows : ファイル削除に失敗 結果3 Linux + NFS : ディレクトリ削除に失敗  結果が全部違う…  しかも、結果3 に至っては、空ディレクトリなのになぜ かディレクトリの削除に失敗している…… 50 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 51.
    Linux と Windowsの動作の違い  UNIXは、オープンしているファイルを削除できる  というか、“delete on last close” という定型コード  削除したファイルはクローズするまで存在が保証されている  Windowsのファイルロックは悲観ロック しかも自動  ファイルをオープンしていたら削除等が出来なくなる  UNIXと同じようには書けない 51 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 52.
    NFSはもっと恐ろしい  UNIXは、ファイルを削除してもファイルにアクセスできる  NFSプロトコルでは、「ファイル名」がないとファイルにアクセス できない  すなわち 「オープンされてるファイルは削除できない」  だけどWindowsと違って「悲観ロックではない」「削除は失敗で きない」  “Silly Rename” (see also: http://nfs.sourceforge.net/ ) 52 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 53.
    Silly Rename  NFS上のファイルをオープン  オープンしたままファイルを削除すると  ファイルは削除されない  temp -> .nfsXXXXに自動的に名前が変更  NFSクライアントプロセス終了時  名前変更されたファイルが自動的に削除される  .nfsXXXX -> 削除 53 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 54.
    発生していた現象  NFS上にファイル作成  NFS上でオープン中のファイルをdelete()  NFSがファイルを.nfsXXXXにリネーム  ディレクトリ削除時、ディレクトリ下にファイル.nfsXXXXが存在  ディレクトリ削除失敗(問題発生)  IOExceptionでプログラム終了  プロセス終了時にNFS側で.nfsXXXX削除 (証拠隠滅完了!暗黙的!全自動!) 54 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 55.
    後からはわからない  再現すればそれほど難しい問題ではない  deleteに失敗する所にブレークポイントを設定  しかし他の環境で起きた場合は?  痕跡は残らないので調査が困難  close忘れという単純なバグでも起きる  JDK7にしよう! try-with-resources を使おう! 55 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 56.
    doSomething (バグフリー) private static void doSomething(File file) throws IOException { try (FileOutputStream os = new FileOutputStream(file)) { os.write("test test test¥n".getBytes()); os.flush(); } } 56 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 57.
    One more thing…  しかし、この問題が本当に恐ろしいのは、バグでなく ても起きる点である。 57 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 58.
    doSomething (mmap ver) private static void doSomething(File file) throws IOException { try (RandomAccessFile ras = new RandomAccessFile(file,"rw")) { FileChannel fc = ras.getChannel(); MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 4096); mbb.put("test test test¥n".getBytes()); }} 58 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 59.
    原因と結果  クローズ忘れていた時と同じ  unmapされてないfileは…  JavaのFileChannelにはmap()はあるけどunmap()がない  unmap()はどこから呼ばれる?  unmap()はいつ呼ばれる?  Reference Handler Thread上で発見 59 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 60.
    unmap時のcall stack (java+ native) #0 0x0000003dc22d0d20 in munmap () from /lib64/libc.so.6 #1 0x00002aaab6870477 in Java_sun_nio_ch_FileChannelImpl_unmap0 () [2] sun.nio.ch.FileChannelImpl$Unmapper.run (FileChannelImpl.java:746) [3] sun.misc.Cleaner.clean (Cleaner.java:142) [4] java.lang.ref.Reference$ReferenceHanlder.run (Reference.java:141) 60 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 61.
    sun.nio.ch.FileChannelImpl public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException { .... Unmapper um = new Unmapper(addr, mapSize, isize, mfd); .... return Util.newMappedByteBufferR( isize, addr + pagePosition, mfd, um); 61 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 62.
    Direct-X-Buffer.java.templete // For memory-mapped buffers -- invoked by // FileChannelImpl via reflection protected Direct$Type$Buffer$RW$(int cap, long addr, FileDescriptor fd, Runnable unmapper) { #if[rw] super(-1, 0, cap, cap, fd); address = addr; cleaner = Cleaner.create(this, unmapper); 62 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 63.
    sun.misc.Cleaner * General-purpose phantom-reference-basedcleaners. * <p> Cleaners are a lightweight and more robust alternative to finalization. * They are lightweight because they are not created by the VM and thus do not  長々といろいろ書いてあるけど要は * require a JNI upcall to be created, and because their cleanup code is * invoked directly by the reference-handler thread rather than by the * finalizer thread. They are more robust because they use phantom references,  クラスライブラリの中からだけつかえる * the weakest type of reference object, thereby avoiding the nasty ordering * problems inherent to finalization.  ファントムリファレンスベースのFinalizerもどき * <p> A cleaner tracks a referent object and encapsulates a thunk of arbitrary * cleanup code. Some time after the GC detects that a cleaner's referent has  でもFinalizerの代わりにはならない * become phantom-reachable, the reference-handler thread will run the cleaner. * Cleaners may also be invoked directly; they are thread safe and ensure that  単純で素直なクリーンアップの時だけ使える * they run their thunks at most once. * <p> Cleaners are not a replacement for finalization. They should be used →unmap()はGCされたら呼ばれる * only when the cleanup code is extremely simple and straightforward.  * Nontrivial cleaners are inadvisable since they risk blocking the * reference-handler thread and delaying further cleanup and finalization. 63 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 64.
    mapがunmapされるとき  MappedBufferがGCされる  ReferenceHandlerスレッドが動き出す  Cleanerに登録されていたUnmapperが呼び出される  munmapシステムコールが呼ばれる 64 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 65.
    邪道しかない  動かす方法はあるが…… _人人人人人人人_ > System.gc(); <  ̄^Y^Y^Y^Y^Y^Y ̄  邪道!実装依存!  邪道!実行時依存!  邪道!タイミング依存! 65 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 66.
    蛇足  実は、本当に起きた現象では、ここでは言い尽くせない理由 のせいで、テコでもファイルディスクリプタが消えず、結局Silly Renameのせいでファイルが消えない問題は解決しなかった  でも… プログラムが動かないときの、プログラマの言い訳 第5位 それ、動かないとホントに困るの? (えっうん)  ユーザは正常に動作させたいだけであって、ディレクトリを消 したいわけではない(場合もある) 66 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 67.
    おわりに 67 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 68.
    まとめ  手元で再現しないバグはある  様々な異なる実装があっても仕様を満たしていればJava  同じインタフェースでも返り値の精度が違うことがある  同じインタフェースでも動作の意味が違うことがある  明確なバグがなくても環境によって動かないことがある  実装の抽象化は大事!でも破綻も付き物!  どこに罠があるかという感覚を養いたい 68 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 69.
    Q&Aコーナー ( ´_ゝ`)フーン 69 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.
  • 70.
    70 Copyright © 2013, Oracle and/or its affiliates. All rights reserved.