スーパーエンジニアの独り言 第12回 『メギドの丘』 【Rubyコラム】
おはようございます。吉政創成の吉政でございます。
弊社のお客様であるCTC教育サービスが標記のコラムを記載したメルマガを発行しました。
今号の目次は以下の通りです。
■キャンペーン Cisco「1コース無料キャンペーン」実施中!!
■新コース システム仮想化基盤構築トレーニング(仮想デスクトップ分野)
MPLS-VPNファーストステップ
■トピック Inst. Tech View ~第14回“Androidマルウェア”~
■コラム スーパーエンジニアの独り言 第12回“メギドの丘”
本メルマガの全文は以下をご覧ください。
ctc20120622.pdfをダウンロード
表記のコラムは以下の通りです。
◆◇ 『スーパーエンジニアの独り言 第12回 “メギドの丘”』 ◇◆
JavaとRubyの例外処理の続きです、前回と併せてお読みくださいませ。前編は基本構文を比較してみましたが、後編はfinally(ensure)節での挙動について言及を試みます。
JavaとRubyを例外処理にて比較した場合にも、やはり実行形態(処理系)が異なる点がコーディングにおいても相違点の一因になると考えられます。Javaではソースコード中にライブラリのメソッドが例外をスロー(送出)し例外チェックが必要な場合に、コード内に例外処理を記述しないとコンパイルエラーになります。例外発生が予期される箇所を事前に教えてくれると理解出来ます。適切な例外ハンドラを用意しなさいという教えを請うのです。
(但し、RuntimeExceptionサブクラス等は例外チェックがありません。)
その反面、Rubyの場合には処理系がインタプリタのためにコンパイル手順そのものがありません。Rubyがスクリプト用途としての簡易実行が可能であり、つまり自由度が大きい分、プログラマにその責務を委ねているとも思えます。(RubyMotionというRubyコンパイラも最近登場です。)
例外発生時には、catch(rescue)節で指定した例外を捕捉して適切な処理を実施すべきですが、catch(rescue)節を省略した場合にはデフォルトのエラー処理がなされます。(よく画面で見かけるあのメッセージです。)その後、プログラムは終了するという事になります。プログラムで例外の発生を回避し処理を継続したい場合には省略せずに捕捉しましょう。
プログラムの終了前に「例外発生の有無に関わり無く」後処理をしたい際、Javaは「お終い(お仕舞い)に(finally)」、Rubyは「確かに(ensure)」とキーワードで指定出来ます。最後に必ず実行してね、と書けるのです。
そこで本題となりますが、
『finally(ensure)節の処理を、いつも実行してくれるのか否か?』
例外を大別すると「致命的なエラー」とそれ以外の二つがあります。プログラムを実行する母体である仮想マシン(インタプリタ)が異常終了するような「致命的なエラー」が発生した場合、プログラムの実行自体が継続出来ないために、コード内で例外を捕捉し後処理を記述したとしても適うことは稀な事でしょう(タイミングや処理系の実装に拠ります)。
Javaでは、JVM(もしくは外部)で発生した「致命的なエラー」は、Errorのサブクラスで表現され、これらは通常捕捉することが出来ません。Rubyでは、インタプリタ内部で「致命的なエラー」が発生した場合"fatal"と表現され、通常プログラムからは参照することが出来ません。
// Javaの例外クラスツリー構造(一部)
Object
└ Throwable
├ Error
│ └ VirtualMachineError
│ ├ OutOfMemoryError
│ └ StackOverflowError
└ Exception
└ RuntimeException
# Rubyの例外クラスツリー構造(一部)
Object
└ Exception
├ fatal
├ NoMemoryError
├ SystemStackError
├ SignalException
├ SystemCallError
├ SystemExit
└ StandardError
└ RuntimeError
JVM(ruby)自身が異常終了する場合、これを捕捉出来ないのは道理ですが、例えば、JVMがメモリ不足(OutOfMemoryError, NoMemoryError)に陥る現象が発生すると当然「致命的なエラー」の仲間ですが、発生原因に拠っては、この例外を捕捉することも可能な場合もあります。捕まえたい場合は、 catch(OutOfMemoryError e) もしくは、rescue NoMemoryError
そして、 finally と ensure も思い切って書くと良いでしょう。捕捉できたならきっと、後処理もしてくれることでしょう。
// Javaで OutOfMemoryError を catchして e.printStackTrace();
java.lang.OutOfMemoryError: Java heap space
at OutOfMemoryTest.main(OutOfMemoryTest.java:9)
# Rubyで NoMemoryError を rescueして p ex
#<NoMemoryError: failed to allocate memory>
スタックオーバーフロー(StackOverflowError, SystemStackError)も同じです。しかしJavaでは別スレッドの処理中にメモリ不足が発生した場合には、そのスレッドが終了してしまうと例外を捕捉出来ません。その際には、UncaughtExceptionHandler(JDK 5 以降)が使えます。ThreadGroupでこのインターフェースが実装されていますので、例外を逃さず捕まえましょう。
最後に、明示的にプログラム終了を指示する場合についてです。Javaで System.exit() を呼び出すと、finally節は実行されません。Rubyでは exit!を呼び出した場合、rescue節は実行されませんが、exit, abort, at_exitメソッドなどで終了する場合には、rescue節はちゃんと実行されます。
これはUNIXのライブラリexit(3)とシステムコール_exit(2)のどちらを呼び出しているのかの違いになります。ライブラリではユーザ空間の後始末をしようとしてくれますが、システムコールはカーネル空間で終了するため、rescue節が無視されてしまうのでしょう。
(UNIXのman頁が参考になります、是非御一読ください。)
尚、詳細については各々の言語仕様を是非ご参照くださいませ。
今回は少しコードをお見せしたいと思ったのですが文字で埋め尽くされてしまいました。またの機会にご紹介したいと思います。次回をお楽しみに。
Ruby関連コースはこちら
http://dm.ctc-g.co.jp/c?c=752&m=11740&v=9dbc0d05
スーパーエンジニアの独り言のバックナンバーはこちらをご覧ください。