既存プロジェクトをJDK8へ移行したら、java.lang.VerifyErrorが出た


目次

Hello World! ソーシャルネットワーク事業部の、jyukutyoこと阪田です。

JDK6で開発し、現在も運用しているプロジェクトをJDK8へ移行させました。最大の理由は6がend of lifeとなっていることです。もちろんエンジニア的には8が使いたくてしょうがないという気持ちもあります。

8のすごさについての説明はすばらしいサイトがたくさんありますので、他へお譲りするとしまして、ここでは僕たちがちょっとハマった、JDK8導入でのTipsをお届けします。

djUnitが…

このプロジェクトでは、ユニットテストにdjUnit(2011年10月からもうバージョンアップはありませんね…)を利用しています。JDKを変えてユニットテストを実行すると、次のようなエラー(テスト失敗ではなく)が発生しました。

    java.lang.VerifyError: Expecting a stackmap frame at branch target 22
Exception Details:
  Location:
    hoge.HogeClass.<init>()V @18: ifnull
  Reason:
    Expected stackmap frame at this location.

        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:340)

java.lang.VerifyError

JDK6で導入されたclassファイルの検証方式変更(JSR-202)がJDK7からは有効となっており、VerifyErrorが発生しました。そのため、このエラーはJDK7でユニットテストを実行したときも発生します。

検索エンジンで調べると「-XX:-UseSplitVerifier」オプションをつけるとよいという情報がたくさん出てきます。このオプションをつけて実行すると、次のようになりました。

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option UseSplitVerifier; support was removed in 8.0

表記のとおりです。JDK8ではこのオプションはサポートされていないので、無視されました。当然結果も同じでした。JDK7では動作するようになるのですが…

noverifyオプション!

UseSplitVerifierは「Use the new type checker with StackMapTable attributes」に関するオプションであり、それにマイナスをつける(-UseSplitVerifier)とその機能をオフにします。オフにしたことでJDK7で動作するようになったということは、8でもこのtype checkerをオフにすれば動作しそうです。このチェックというのは、classファイル(バイトコード)の検証です。そこでJVMのオプションを調べると「noverify」がありました。

クラスがバイトコードの検証なしにロードされます。

セキュリティの問題など、さすがにプロダクトコードには使用したくないオプションですが、ユニットテストの実行くらいなら問題ないと考えました。「-noverify」オプションをつけてユニットテストを実行します。

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------

成功しました!

そもそもの原因

djUnitは、モックの実現のためにバイトコードを書き換えています。デフォルトではASMを利用しています。このASMのバージョンが古いと、新しいclassファイルの検証方式に対応しておらず、エラーとなってしまいます。

そのためASMのバージョンを最新にすれば、テストが成功するのでは!と考えて試してみました。しかし、ASMもメジャーバージョンアップしていますので、djUnitの方が対応しておらず、エラーとなりました。

結び

やっぱり新しいJDKは最高です!!みなさんもぜひバージョンアップしましょう!たとえ新しい構文がまだ使いこなせないとしても、JVMを新しくするだけでアプリケーションの実行速度も向上しますから!