SlideShare uma empresa Scribd logo
1 de 70
Baixar para ler offline
それでも環境依存は残っている
                     起きたり起きなかったりする問題のお話

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.

Mais conteúdo relacionado

Mais procurados

【12/5 最新版】AWS Black Belt Online Seminar AWS re:Invent 2018 アップデート情報
【12/5 最新版】AWS Black Belt Online Seminar AWS re:Invent 2018 アップデート情報【12/5 最新版】AWS Black Belt Online Seminar AWS re:Invent 2018 アップデート情報
【12/5 最新版】AWS Black Belt Online Seminar AWS re:Invent 2018 アップデート情報Amazon Web Services Japan
 
サポート エンジニアが Azure Networking をじっくりたっぷり語りつくす会
サポート エンジニアが Azure Networking をじっくりたっぷり語りつくす会サポート エンジニアが Azure Networking をじっくりたっぷり語りつくす会
サポート エンジニアが Azure Networking をじっくりたっぷり語りつくす会ShuheiUda
 
Zabbix最新情報 ~Zabbix 6.0に向けて~ @OSC2021 Online/Fall
Zabbix最新情報 ~Zabbix 6.0に向けて~ @OSC2021 Online/FallZabbix最新情報 ~Zabbix 6.0に向けて~ @OSC2021 Online/Fall
Zabbix最新情報 ~Zabbix 6.0に向けて~ @OSC2021 Online/FallAtsushi Tanaka
 
Unified JVM Logging
Unified JVM LoggingUnified JVM Logging
Unified JVM LoggingYuji Kubota
 
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)NTT DATA Technology & Innovation
 
JVMのGCアルゴリズムとチューニング
JVMのGCアルゴリズムとチューニングJVMのGCアルゴリズムとチューニング
JVMのGCアルゴリズムとチューニング佑哉 廣岡
 
20190828 AWS Black Belt Online Seminar Amazon Aurora with PostgreSQL Compatib...
20190828 AWS Black Belt Online Seminar Amazon Aurora with PostgreSQL Compatib...20190828 AWS Black Belt Online Seminar Amazon Aurora with PostgreSQL Compatib...
20190828 AWS Black Belt Online Seminar Amazon Aurora with PostgreSQL Compatib...Amazon Web Services Japan
 
[Aurora事例祭り]Amazon Aurora を使いこなすためのベストプラクティス
[Aurora事例祭り]Amazon Aurora を使いこなすためのベストプラクティス[Aurora事例祭り]Amazon Aurora を使いこなすためのベストプラクティス
[Aurora事例祭り]Amazon Aurora を使いこなすためのベストプラクティスAmazon Web Services Japan
 
Java 9で進化する診断ツール
Java 9で進化する診断ツールJava 9で進化する診断ツール
Java 9で進化する診断ツールYasumasa Suenaga
 
AWSのログ管理ベストプラクティス
AWSのログ管理ベストプラクティスAWSのログ管理ベストプラクティス
AWSのログ管理ベストプラクティスAkihiro Kuwano
 
Spring Boot の Web アプリケーションを Docker に載せて AWS ECS で動かしている話
Spring Boot の Web アプリケーションを Docker に載せて AWS ECS で動かしている話Spring Boot の Web アプリケーションを Docker に載せて AWS ECS で動かしている話
Spring Boot の Web アプリケーションを Docker に載せて AWS ECS で動かしている話JustSystems Corporation
 
20190220 AWS Black Belt Online Seminar Amazon S3 / Glacier
20190220 AWS Black Belt Online Seminar Amazon S3 / Glacier20190220 AWS Black Belt Online Seminar Amazon S3 / Glacier
20190220 AWS Black Belt Online Seminar Amazon S3 / GlacierAmazon Web Services Japan
 
20180704 AWS Black Belt Online Seminar Amazon Elastic File System (Amazon EFS...
20180704 AWS Black Belt Online Seminar Amazon Elastic File System (Amazon EFS...20180704 AWS Black Belt Online Seminar Amazon Elastic File System (Amazon EFS...
20180704 AWS Black Belt Online Seminar Amazon Elastic File System (Amazon EFS...Amazon Web Services Japan
 
Always on 可用性グループ 構築時のポイント
Always on 可用性グループ 構築時のポイントAlways on 可用性グループ 構築時のポイント
Always on 可用性グループ 構築時のポイントMasayuki Ozawa
 
JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)
JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)
JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)Yoshiro Tokumasu
 
PostgreSQL16でのロールに関する変更点(第41回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQL16でのロールに関する変更点(第41回PostgreSQLアンカンファレンス@オンライン 発表資料)PostgreSQL16でのロールに関する変更点(第41回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQL16でのロールに関する変更点(第41回PostgreSQLアンカンファレンス@オンライン 発表資料)NTT DATA Technology & Innovation
 
MHA for MySQLとDeNAのオープンソースの話
MHA for MySQLとDeNAのオープンソースの話MHA for MySQLとDeNAのオープンソースの話
MHA for MySQLとDeNAのオープンソースの話Yoshinori Matsunobu
 

Mais procurados (20)

Mavenの真実とウソ
Mavenの真実とウソMavenの真実とウソ
Mavenの真実とウソ
 
【12/5 最新版】AWS Black Belt Online Seminar AWS re:Invent 2018 アップデート情報
【12/5 最新版】AWS Black Belt Online Seminar AWS re:Invent 2018 アップデート情報【12/5 最新版】AWS Black Belt Online Seminar AWS re:Invent 2018 アップデート情報
【12/5 最新版】AWS Black Belt Online Seminar AWS re:Invent 2018 アップデート情報
 
サポート エンジニアが Azure Networking をじっくりたっぷり語りつくす会
サポート エンジニアが Azure Networking をじっくりたっぷり語りつくす会サポート エンジニアが Azure Networking をじっくりたっぷり語りつくす会
サポート エンジニアが Azure Networking をじっくりたっぷり語りつくす会
 
Zabbix最新情報 ~Zabbix 6.0に向けて~ @OSC2021 Online/Fall
Zabbix最新情報 ~Zabbix 6.0に向けて~ @OSC2021 Online/FallZabbix最新情報 ~Zabbix 6.0に向けて~ @OSC2021 Online/Fall
Zabbix最新情報 ~Zabbix 6.0に向けて~ @OSC2021 Online/Fall
 
Unified JVM Logging
Unified JVM LoggingUnified JVM Logging
Unified JVM Logging
 
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
 
JVMのGCアルゴリズムとチューニング
JVMのGCアルゴリズムとチューニングJVMのGCアルゴリズムとチューニング
JVMのGCアルゴリズムとチューニング
 
20190828 AWS Black Belt Online Seminar Amazon Aurora with PostgreSQL Compatib...
20190828 AWS Black Belt Online Seminar Amazon Aurora with PostgreSQL Compatib...20190828 AWS Black Belt Online Seminar Amazon Aurora with PostgreSQL Compatib...
20190828 AWS Black Belt Online Seminar Amazon Aurora with PostgreSQL Compatib...
 
Azure Network 概要
Azure Network 概要Azure Network 概要
Azure Network 概要
 
[Aurora事例祭り]Amazon Aurora を使いこなすためのベストプラクティス
[Aurora事例祭り]Amazon Aurora を使いこなすためのベストプラクティス[Aurora事例祭り]Amazon Aurora を使いこなすためのベストプラクティス
[Aurora事例祭り]Amazon Aurora を使いこなすためのベストプラクティス
 
Java 9で進化する診断ツール
Java 9で進化する診断ツールJava 9で進化する診断ツール
Java 9で進化する診断ツール
 
AWSのログ管理ベストプラクティス
AWSのログ管理ベストプラクティスAWSのログ管理ベストプラクティス
AWSのログ管理ベストプラクティス
 
AWS CLIでAssumeRole
AWS CLIでAssumeRoleAWS CLIでAssumeRole
AWS CLIでAssumeRole
 
Spring Boot の Web アプリケーションを Docker に載せて AWS ECS で動かしている話
Spring Boot の Web アプリケーションを Docker に載せて AWS ECS で動かしている話Spring Boot の Web アプリケーションを Docker に載せて AWS ECS で動かしている話
Spring Boot の Web アプリケーションを Docker に載せて AWS ECS で動かしている話
 
20190220 AWS Black Belt Online Seminar Amazon S3 / Glacier
20190220 AWS Black Belt Online Seminar Amazon S3 / Glacier20190220 AWS Black Belt Online Seminar Amazon S3 / Glacier
20190220 AWS Black Belt Online Seminar Amazon S3 / Glacier
 
20180704 AWS Black Belt Online Seminar Amazon Elastic File System (Amazon EFS...
20180704 AWS Black Belt Online Seminar Amazon Elastic File System (Amazon EFS...20180704 AWS Black Belt Online Seminar Amazon Elastic File System (Amazon EFS...
20180704 AWS Black Belt Online Seminar Amazon Elastic File System (Amazon EFS...
 
Always on 可用性グループ 構築時のポイント
Always on 可用性グループ 構築時のポイントAlways on 可用性グループ 構築時のポイント
Always on 可用性グループ 構築時のポイント
 
JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)
JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)
JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)
 
PostgreSQL16でのロールに関する変更点(第41回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQL16でのロールに関する変更点(第41回PostgreSQLアンカンファレンス@オンライン 発表資料)PostgreSQL16でのロールに関する変更点(第41回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQL16でのロールに関する変更点(第41回PostgreSQLアンカンファレンス@オンライン 発表資料)
 
MHA for MySQLとDeNAのオープンソースの話
MHA for MySQLとDeNAのオープンソースの話MHA for MySQLとDeNAのオープンソースの話
MHA for MySQLとDeNAのオープンソースの話
 

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

  • 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 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.
  • 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 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.
  • 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-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.
  • 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.