yamadamn’s blog

IT関連技術で経験したこと・気になったことをたまに書きます

3/13出版予定の共著『みんなのJava』でJDKディストリビューションについて書きました #minjava

Twitterで既に告知しましたが、共著『みんなのJava』が3/13に発売予定となりました。

著者6名のうち、いわゆるSIerに勤務しているのは私だけで、Javaが様々なところで使われており、改めて非常に大きな存在であると思います。

私は2章の「JDKディストリビューション徹底解説」を担当させていただきました。

今まではOracle JDK/JREのみを使っていた方も多いと思いますが、OpenJDKをベースとして多くのJDKディストリビューションが登場してきました。 「Oracle社によるJava」ではなく「みんなのJava」になったことが、こうした面からも少しでも多くの方に伝われば幸いです。

これは基本的に、昨年5月にOracle Code TokyoやJJUG CCCアンカンファレンスでお話した内容がベースになっています。 ちょうどOracle Code Tokyoの登壇依頼があったのと同じ時期くらいに、きしださんから書籍についてもお声掛けをいただきました。

当初は昨年中(令和元年)に出すような話だったのが、何だかんだでずれてしまいましたが、無事に出版にこぎつけて何よりです。 技術評論社の細谷さんや小竹さんには大変お手数やご心配をお掛けしましたが感謝を申し上げます。ありがとうございました。

昨年の振り返り

幸いにも昨年は各地イベントでOracle Code Tokyo/JJUG CCCでの内容をブラッシュアップしてお話する機会をいただきました。

7月のイベント内容は次のTogetterでまとめています。 togetter.com

その後、10月〜11月頭にかけてもお話ししています。

togetter.com togetter.com 素敵なグラレコも書いていただきました。

www.slideshare.net

こうした内容を書籍化するうえで、できる限り最新の情報を反映し、プロの編集者の意見も踏まえながら加筆修正しています。 スライド資料だけでは説明不足だったところも、書籍をご一読いただければ、ご理解いただけるのではないかと考えています。

今後のJDKディストリビューション

さて、今後のJDKディストリビューションがどうなっていくかについて、軽く予測してみます。

まずは2020年12月に個人ユーザ向けのOracle JDK/JREのPublic Updatesが終了する予定となっています。 Public Updatesが終了すると、Oracle JDK 8の最新版がOTNからダウンロードできなくなる可能性があります。 そして、エンドユーザ向けPublic JREを配布している java.com サイトについても、その役目を終えるかもしれません。 もちろんまだ延期される可能性もあるのですが、終了時には割と大きなインパクトを与えるでしょう。 ただ、こうした状況も今までと同様にJavaコミュニティの力で乗り越えていけると考えています。

また、AdoptOpenJDKを利用しているところは、かなり増えてきた実感がありますが、盲目的に追従するのは懸念もあります。 Javaのエコシステムにおいては非常に大きな位置づけとなりましたが、TCKに通っていなかったり、商標の観点から、個人的には一歩引いて見るようにしています。 スポンサーの面などから、しばらくは安泰と言えそうですが、今後にどうなっていくかは注目したいところです。

JDKディストリビューションは基本的に環境・状況次第で乗り換えていくのがよいと考えていますが、書籍がその一助になれば幸いです。

5/17 Oracle Code Tokyo, 5/18 JJUG CCCアンカンファレンスでOpenJDKディストリビューションの選び方について話してきました

タイトル通りのイベントで話してきました。

ひとまずTogetterでまとめてありますので資料などもそちらからどうぞ。

ここ半年以上追ってきたトピックであり、ツイートしてきた内容の集大成かなと個人的には思っています。 GW10連休の前半は関連するツイートをまとめて、後半に資料に起こした感じですね。 おかげさまで「OpenJDKソムリエ」タグのつくまとめが多く出来上がりましたw

togetter.com

セッション資料の裏表紙にも記載しているのですが、これらはJava関連コミュニティの方々や関係者各位も含め、 多くの皆様とのやり取りやご協力によって生まれたものです。改めて感謝を申し上げます。

残念ながらまだJava/JVM界隈では混乱がしばらく続きそうですので、もし地方の勉強会やカンファレンスでも需要があるようでしたら、OpenJDKソムリエ出張編として伺いますのでお気軽にお声がけください!


さて、簡単にですが当日2日間を振り返ります。

5/17 Oracle Code Tokyo

Oracle Code Tokyoの前身であるJava Day Tokyoに2015年にスポンサーセッションで登壇した以来の大きなイベントでした。

今回は参加者層も2015年とはまったく異なりましたが、おかげさまで満員御礼となりました。

Java界隈の強い面々が前列に集まり、始まるまではかなり緊張しましたが、多くの方に興味を持って参加いただいたようで何よりです。


実は「www.oracle.co.jp」ドメイン内で、はてなブックマーク数順としては1・2番目とも私の資料となりました。 2015年のセッション資料の方がまだ数が多いのですが、間違いなく今回の方が力を入れているつもりです。

b.hatena.ne.jp

登壇の機会を与えていただいた日本オラクル伊藤敬さんや関係者の皆様ありがとうございました。
また、今まで直接面識がなくTwitterでやり取りしていた方々も当日のイベントに来ていただいてご挨拶できたり、その後の飲み会も盛り上がり、楽しい一日を過ごすことができました。

若干心残りだったのは英語インタビューの承諾が遅れてしまった(割と直前の案内なのに締切早すぎでしたが…)ために、当日に受けれなかったことと、メディアスポンサーが多かったにも関わらず、私のセッションには誰も取材に来てくれなかったことですかね。後者について大手メディアの影響はやはり大きいもので、Oracle側の主張しか取り上げないことで "Java 有償化"の「誤解」が今も続いている状態を度々観測しています…

5/18 JJUG CCC

JJUG CCCセッションも今はかなり激戦区となってしまいましたが、2013 Fall以来の話す側としてアンカンファレンスにネタを出してみました。
前日のイベントでは基本的に一方通行で内容をお伝えする側でしたが、あまり公の場では話せないような、ある程度ぶっちゃけた各ディストリビューターの動向を話したり、参加者の方々の意見なども聞いてみたかった次第です。

CCC初参加者向けのランチセッションで、実行委員長(前会長)の鈴木雄介さんから、いきなり「OpenJDKソムリエ」の紹介(?)があったらしいのには驚きましたw

ちなみに持田さんは「OpenJDKソムリエ」の名付け親です。


前日のOracle Code TokyoとJJUG CCCでは参加者層も異なり、アンカンファレンスのテーマにも多くの方が投票していただいたおかげで、こちらも盛況となりました。 司会進行を務めていただいた寺田佳央さんをはじめ、スタッフの皆様や参加者の皆様ありがとうございました。

寺田さんには前日のイベントにも参加いただき、当日にはうまく伝えられなかったのですが、各OpenJDKディストリビューションを調べているモチベーションは次のような感じです。

セッションやTogetterまとめの隠れテーマでもあるのですが、Java/JVM関連コミュニティやエコシステムの重要性は本当に大きいと感じており、微力ながらも貢献できれば幸いです。

もちろん他のセッションや懇親会も非常に楽しかったです。JJUG CCCの二次会に行ったのも数年ぶりな気がしますが、おかげで海外のJava Championをはじめ多くの方と話す機会が得られたのは貴重な機会でした。

今後もJJUGでは英語セッションを増やしていく方向性のようなので、ますます英語を頑張らないとなぁ、とは思っていますが、私個人としては日本ファースト(?)の精神で当面は頑張りたい気もしています。


どちらのイベントも共通することですが、一参加者としてだけではなく、登壇する側やスタッフに回ると、(大変ですが)より楽しめることを実感した二日間でした。

過去の経緯なども振り返ろうと思ったのですが、あまりに長くなってしまうので別の機会に。

Software Design 2019年1月号 - 第2特集 第1章 "Javaのバージョンアップと付き合っていくために" へのコメントあるいは正誤表

前回の記事で、参考資料をいくつか挙げました。 yamadamn.hatenablog.com その中でも先日発売された『Software Design 2019年1月号』の第2特集 "Javaのバージョン問題に前向きに取り組む方法" の第1章 "Javaのバージョンアップと付き合っていくために" が、現段階でまとまっており、非常に有用だと思います。 ただ、いくつか細かいところで訂正したい箇所がありました。

という訳で、正誤表レベルですが記事として残してみます。

もちろん実際に書籍として執筆する際には、紙面やスケジュールの都合・バランスなど、様々なトレードオフがあったことは想像に難くありません。 そのため、あくまで私が事前にレビューするとしたら、この辺りをコメントしたかもしれない、とご理解ください。

以降、基本的に、見出し(節)や該当箇所を部分的に引用(ページ番号含む)し、コメントしていきます。

前書き

全体としてJavaが有償化することは今のところないので安心してください (P62)

"今のところ"だと、今後有償化される可能性もあると、若干不安を煽る日本語かもしれないので余計かもしれません。 考察は、なぎせさんの記事 Javaのリスク考察 2018年版 - プログラマーの脳みそ が詳しいですが、"Javaが有償化することはない" と言い切った方がよいかもです。

Javaの何が変わったのか?

Javaのリリースに関する変化

▼表1 Oracle JDKOracle OpenJDKのサポートの違い (P63)

Java 6の[前バージョンからの期間]が「1年3ヵ月」となっていますが「2年3ヵ月」が正しいです。

Oracleが提供するバイナリの変化

この2つのバイナリは内容的にはまったく同じものですが (P64)

実際には細かいところで、java -versionの出力や配布物(主にライセンス情報)など異なるため、"まったく同じ"訳ではなく、"ほぼ同じ"くらいが適切かと思います。

OpenJDKを提供するベンダとそのサポート内容

たとえばRed Hatや、Azul System、AdoptOpenJDKプロジェクトなどです。 (P64)

Azul Systems と"s"がつくのが正確ですね。AdoptOpenJDKも"プロジェクト"というよりはコミュニティといったほうが正確かもしれません。

▼表3 Oracle JDKOracle OpenJDKのサポートの違い (P64)

Oracle JDKの[リリース]が「3年ごと(LTSだけ)」となっていますが、実際にはnon-LTSも出てサポート期間(Premier Support)が6ヵ月です。 LTSの[アップデート提供]も「5年から最長8年」となっていますが、これは"最短"の期間であり、延びる可能性があります。 (元資料から消えてしまったので微妙ですが、魚拓には"At least"で注意書きがあります。)

こうした点は他のベンダーも似たようなものですが、書き方として「非LTSは6ヵ月、LTSは最短5年から8年」としたほうが個人的にはしっくり来ます。

COLUMN: サポートとベンダ

注A) IBMやSAPなど、OpenJDKではない独自実装 (P65)

SAPのSapMachine一部の違いはあるもののOpenJDKベースです。

▼表4 ベンダが提供するOpenJDK (P64)

先の表3のコメントと同様です。 またタイミング的にどうしようもないのですが、Red HatはWindows版のOpenJDKについて商用サポートを提供することが最近発表されました。

Javaのバージョンアップに対する方針 (P69-71)

大きく3つの方針が紹介されていますが、基本的にAdoptOpenJDKなどによる無償のアップデートを前提とした記載となっていると思います。

有償サポートを締結したうえで、もう少し長い期間(システムの寿命に合わせるが5年以上)使い続けてからバージョンアップする選択肢も割とあると思います。 方針としては "3、4年ごとにバージョンアップする" とそれほど変わりはないのですが、業務システムではもう少し慎重なユーザも多いと思います。 西野さんのインタビュー記事 "「Java 8はいつまで使い続けていいんですか?」Georges Saab氏に聞いたJavaのこの先" が肌感覚としては近いかもしれません。

"Javaのサポートポリシー変更等に関する技術レポート" に意見してみた

実に2年ぶりのブログを書いてみます。ついでなので、はてなダイアリーから移行してみました。

Javaのリリースサイクル変更に関連して、一通り情報が出揃ってきました。 一部では日経xTECHなどで相変わらず酷い記事量産して不安を煽り立てていますが、Javaは今も無償であり、Oracle JDK/JRE以外にも様々な選択肢があることは今更言うまでもありません。

さて、日本では政府CIOがまとめた標準ガイドライン群があり、官公庁の情報システム向けの指針を提示しています。 この中に技術レポートとして "Javaのサポートポリシー変更等に関する技術レポート"1があります。 "2018年4月時点の情報に基づき" と記載されていますが、無償で利用するための選択肢がOracle OpenJDKについてしか触れられていないように見えます。

各種ガイドへのご意見はこちらまでお願いします。 内閣官房 情報通信技術(IT)総合戦略室 E-Mail:i.it-dashboard_atmark_cas.go.jp

※迷惑メール防止のため、atmarkは@に置き換えてください。

とのことだったので、今更感はありますが、以下の通り意見を出してみました。

内閣官房 情報通信技術(IT)総合戦略室 ご担当者様

お世話になります。山田と申します。
https://cio.go.jp/sites/default/files/uploads/documents/technique_report_java.pdf
こちらで掲載されているレポートについて意見したく、メールを差し上げました。

5月に本レポートが発表されてから、様々な動きがあり、内容が古くなっていると
見受けられますので、改訂をいただきたく存じます。

具体的には、[3 サーバー環境向けJavaへの対応] の [3.3 対応方針]  (P5)において、
有償サポートもしくはOracle OpenJDK(無償だが半年サイクル)の選択肢のみあるような
書き方がなされておりますが、無償であっても次のような複数の選択肢が存在します。
a) AdoptOpenJDK (MicrosoftやIBMなどがスポンサー)
b) Zulu Community Builds (from Azul Systems)
c) Amazon Corretto (from AWS, 現時点ではプレビュー版)

これらにはいわゆるテクニカルサポート(ベンダーの問合せ対応)は含まれませんが、
LTSでの更新版(アップデート)は、概ね4年以上は提供される見込みです。
これらの無償版を利用する選択肢を 3.3→1)→(2)(3)に加えていただきたく存じます。
※実際のところ a) b) については、本レポートが発表される以前から公表されており、
 選択肢として当初から加えるべきであったと考えております。

なお、それ以外にも用語の面で、"Java"とOracle JDK/JREを同一視していたり、
"OpenJDK"がOracle固有のバイナリのみを示すような書き方がなされていますが、
これらも参考資料をもとに、より正確中立な書き方をなされることも検討ください。

■参考資料
Software Design 2019年1月号 - 第2特集
https://gihyo.jp/magazine/SD/archive/2019/201901

各JDKベンダの動向を知ってJava 11に備えよう
https://gihyo.jp/news/report/2018/10/0501

「Java is Still Free」-Javaのサポート問題へ終止符、迎える4つの進化
https://codezine.jp/article/detail/11258

JDK、Oracle JDK、OpenJDK、Java SEってなに?
https://qiita.com/nowokay/items/c1de127354cd1b0ddc5e

JDKの長期商用サポート(LTS)の提供ベンダー比較(無償利用についても言及あり)
https://qiita.com/u-tanick/items/bb166929a58a4c20bb88

Javaのサポートについてのまとめ
https://qiita.com/nowokay/items/edb5c5df4dbfc4a99ffb

なお、上記の参考資料は、民間Javaコミュニティの有志にてまとめられたものであり、
本レポートの [4 参考情報へのリンク] として記載されているOracle社のみの情報より、
公平中立性が保たれていると考えております。

また、いくつかの参考資料からリンクされていますが、世界各国のJava Championが
まとめた「Java is Still Free」もご確認いただければと存じます。
(英語版) https://docs.google.com/document/d/1nFGazvrCvHMZJgFstlbzoHjpAVwv5DEdnaBr_5pKuHo/edit
(日本語版) https://docs.google.com/document/d/1HtUnuAkUEDGL2gwUOkrDrmLe_zrD6wpAyqYBZxRmHv4/edit

以上、よろしくお願い申し上げます。

xTECHなども、この政府CIOから出されているレポートに影響を受けている可能性もあるため、少しでも誤解や偏見をなくしていければと思います。

なお、個人的な話として、Oracle ACEは今年卒業しましたが、今後もJavaコミュニティに微力ながら貢献できればと考えています。 一部の方から、先日 "OpenJDK警察"との称号(?)を賜りましたが、今回の意見はその活動の一環とも呼べるかもしれません。


  1. 改訂版がリリースされたためリンク切れになってます。

WebLogic Server 12.2.1に密かに追加されたクラスデータ共有機能 #jpoug

はじめに

この記事はJPOUG Advent Calendarの10日目です。
先日10/19にWebLogic Server(以下、WLS)勉強会で、WLS 12.2.1.0/12.2.1.1のマイナーな新機能などを取り上げました。

その際、メジャーな新機能であるマルチテナントに関連しそうな内容はあえて扱わなかった*1のですが、本記事では、おそらく関連するであろうクラスデータ共有機能について取り上げます。
"おそらく"と言うのは、WLSのクラスデータ共有機能については、今のところ公式ドキュメントに記載がないためです。
そのため正式にはサポートされておらず、本記事で記載する内容は、将来的に予告なく変更されたり廃止される可能性もありますので、念のためご注意・ご了承ください。

確認した環境は次の通りです。

クラスデータ共有とは

クラスデータ共有(CDS)は、HotSpot JDKの機能*4として、実はJ2SE 5.0の頃からあります。
詳細はドキュメントを確認いただきたいのですが、アプリケーションの起動時間やメモリフットプリントを短縮するために、複数のJVMでクラスデータを利用できるよう共有アーカイブを作成・利用します。
ただ、通常のCDSは、HotSpot Client VMかつシリアルガベージコレクタ環境でのみのサポートされるもので、今の時代となっては実質的に使われないでしょう。

Oracle JDK 8u40ではこのCDSを拡張して、多くのアプリケーション環境で利用できるように、商用機能*5の一つとして、アプリケーションクラスデータ共有(AppCDS)が導入されました。ツールドキュメントには実験的と記載があるのですが、実際はそんなことはないようです。
今回紹介するWLSのクラスデータ共有は、実際にはこちらのAppCDSを利用することになります。

WLSでのAppCDSの利用方法

それでは、WLSでAppCDSを利用してみましょう。
まずはWLSをインストールし、ドメインを作成してください。
インストール手順やドメイン作成手順は割愛します。

クラスリストの作成

ターミナルからドメインディレクトリに移動し、WLSを起動するためのスクリプト(startWebLogic.sh)に、引数「generateClassList」を加えて実行します。

$ cd ~/weblogic/wls12212_dev/user_projects/domains/base_domain
$ ./bin/startWebLogic.sh generateClassList

すると、通常のWLS起動オプションに次のJVMオプションが加わって実行されます。(改行を入れています)

-XX:+UnlockCommercialFeatures
-XX:+IgnoreEmptyClassPaths
-XX:DumpLoadedClassList=/Users/takahiro/weblogic/wls12212_dev/user_projects/domains/base_domain/WebLogic.classlist
-XX:+UseAppCDS

WLSがRUNNINGで起動したことを確認したら、一旦Ctrl+Cなどで止めてみましょう。
ドメインディレクトリ直下にWebLogic.classlistが作成されていることが確認できます。

$ ls -l
total 1352
-rw-r-----   1 takahiro  staff  671492 12 10 20:24 WebLogic.classlist
drwxr-x---   3 takahiro  staff     102 12  7 01:01 autodeploy
drwxr-x---  20 takahiro  staff     680 12  7 01:01 bin
drwxr-x---   3 takahiro  staff     102 12  7 01:01 common
drwxr-x---  11 takahiro  staff     374 12  7 01:01 config
drwxr-x---   3 takahiro  staff     102 12  7 01:01 console-ext
-rw-r-----   1 takahiro  staff     438 12 10 20:24 derby.log
-rw-r-----   1 takahiro  staff     104 12 10 20:24 derbyShutdown.log
-rw-r-----   1 takahiro  staff     139 12 10 20:24 edit.lok
-rw-r-----   1 takahiro  staff     327  6 13 07:29 fileRealm.properties
drwxr-x---  14 takahiro  staff     476 12  7 01:01 init-info
drwxr-x---   3 takahiro  staff     102 12  7 01:01 lib
drwxr-x---   4 takahiro  staff     136 12  7 01:01 nodemanager
drwxr-x---   3 takahiro  staff     102 12  7 01:01 orchestration
drwxr-x---   7 takahiro  staff     238 12  7 01:01 security
drwxr-x---   3 takahiro  staff     102 12  7 01:01 servers
-rwxr-x---   1 takahiro  staff     287 12  7 01:01 startWebLogic.sh

中身はテキスト形式で、ロードされたクラスが記録されています。
大きいので、最初と最後の10行だけ見てみましょう。

$ head WebLogic.classlist 
java/lang/Object
java/lang/String
java/io/Serializable
java/lang/Comparable
java/lang/CharSequence
java/lang/Class
java/lang/reflect/GenericDeclaration
java/lang/reflect/AnnotatedElement
java/lang/reflect/Type
java/lang/Cloneable
$ tail WebLogic.classlist 
weblogic/messaging/common/PrivilegedActionUtilities$4
weblogic/rmi/server/UnicastRemoteObject
weblogic/rmi/server/RemoteServer
weblogic/rmi/server/RemoteObject
weblogic/messaging/dispatcher/DispatcherWrapperState
weblogic/work/concurrent/services/PartitionConcurrrentManagedObjectFactory$1
weblogic/store/admin/JMXUtils$2
weblogic/store/io/file/StoreFile$1CloseChannels
weblogic/store/admin/JMXUtils$4
weblogic/ldap/LDAPExecuteRequest

Java SEの標準APIやWLSのクラスがリストされているのが分かりますね。

共有アーカイブの作成

次に前項で作成されたWebLogic.classlistを使って、共有アーカイブを作成するためにgenerateArchive.shを実行します。

$ ./bin/generateArchive.sh

実際には、次のようなjavaコマンドが実行されます。(改行を入れておきます)

/Library/Java/JavaVirtualMachines/jdk1.8.0_112.jdk/Contents/Home/bin/java
-XX:+UnlockCommercialFeatures
-XX:+UnlockDiagnosticVMOptions
-Xshare:dump
-XX:+UseAppCDS
-XX:+IgnoreEmptyClassPaths
-XX:+TraceClassPaths
-XX:+IgnoreUnverifiableClassesDuringDump
-XX:SharedArchiveFile=/Users/takahiro/weblogic/wls12212_dev/user_projects/domains/base_domain/WebLogic.jsa
-XX:SharedClassListFile=/Users/takahiro/weblogic/wls12212_dev/user_projects/domains/base_domain/WebLogic.classlist

このコマンドの出力は非常に多くなるため、最後の部分のみ記載します。

Rewriting and linking classes: done
Number of classes 15293
    instance classes   = 15279
    obj array classes  =     6
    type array classes =     8
Calculating fingerprints ... done. 
Removing unshareable information ... done. 
Shared Lookup Cache Table Buckets = 8216 bytes
Shared Lookup Cache Table Body = 617592 bytes
ro space:  48183744 [ 40.5% of total] out of 122683392 bytes [39.3% used] at 0x0000000800000000
rw space:  62089888 [ 52.2% of total] out of 157286400 bytes [39.5% used] at 0x0000000807500000
md space:   8538968 [  7.2% of total] out of  28311552 bytes [30.2% used] at 0x0000000810b00000
mc space:     34053 [  0.0% of total] out of   6291456 bytes [ 0.5% used] at 0x0000000812600000
total   : 118846653 [100.0% of total] out of 314572800 bytes [37.8% used]

そして、共有アーカイブとしてWebLogic.jsaが作成されたことが確認できます。

$ ls -l
total 233512
-rw-r-----   1 takahiro  staff     671492 12 10 20:24 WebLogic.classlist
-r--r-----   1 takahiro  staff  118865920 12 10 20:24 WebLogic.jsa
drwxr-x---   3 takahiro  staff        102 12  7 01:01 autodeploy
drwxr-x---  20 takahiro  staff        680 12  7 01:01 bin
drwxr-x---   3 takahiro  staff        102 12  7 01:01 common
drwxr-x---  11 takahiro  staff        374 12  7 01:01 config
drwxr-x---   3 takahiro  staff        102 12  7 01:01 console-ext
-rw-r-----   1 takahiro  staff        438 12 10 20:24 derby.log
-rw-r-----   1 takahiro  staff        104 12 10 20:24 derbyShutdown.log
-rw-r-----   1 takahiro  staff        139 12 10 20:24 edit.lok
-rw-r-----   1 takahiro  staff        327  6 13 07:29 fileRealm.properties
drwxr-x---  14 takahiro  staff        476 12  7 01:01 init-info
drwxr-x---   3 takahiro  staff        102 12  7 01:01 lib
drwxr-x---   4 takahiro  staff        136 12  7 01:01 nodemanager
drwxr-x---   3 takahiro  staff        102 12  7 01:01 orchestration
drwxr-x---   7 takahiro  staff        238 12  7 01:01 security
drwxr-x---   3 takahiro  staff        102 12  7 01:01 servers
-rwxr-x---   1 takahiro  staff        287 12  7 01:01 startWebLogic.sh
共有アーカイブを利用してWLSを起動

では、仕上げに前項で作成されたWebLogic.jsaを利用してWLSを起動しましょう。
startWebLogic.shの引数に「useArchive」を加えます。

$ ./bin/startWebLogic.sh useArchive

今度は、通常のWLS起動オプションに次のJVMオプションが加わって実行されます*6。(改行を入れています)

-XX:+UnlockCommercialFeatures
-Xshare:auto
-XX:+UseAppCDS
-XX:+IgnoreEmptyClassPaths
-XX:SharedArchiveFile=/Users/takahiro/weblogic/wls12212_dev/user_projects/domains/base_domain/WebLogic.jsa
-showversion

これで共有アーカイブを利用できるようになったはずです。

…と思ったのですが「-verbose:class」を付加して確認すると、共有アーカイブではなく、通常のjarファイルからロードされています。
何かがおかしいはずなので、上記の「-Xshare:auto」を「-Xshare:on」になるようstartWebLogic.shを修正してから実行すると、次のエラーで異常終了しました。

An error has occurred while processing the shared archive file.
Tool agent requires sharing to be disabled.
Error occurred during initialization of VM
Unable to use shared archive.

エージェント利用時は共有アーカイブを無効にする必要があるようですね。ここで思い出しました。
WLS 12.2.1には動的デバッグパッチ機能が追加されており、デフォルトで起動オプションに「-javaagent:$WL_HOME/server/lib/debugpatch-agent.jar」が追加されます。
最初のスライドのP18に記載していますが、これを無効にすべくstartWebLogic.shの引数に「disableDebugPatches」を追加して実行してみましょう。

$ ./bin/startWebLogic.sh useArchive disableDebugPatches

「-verbose:class」の出力を確認すると、次のようにWLS関連のクラスも共有アーカイブから読み込まれるようになりました!*7

[Loaded weblogic.Server from shared objects file by sun/misc/Launcher$AppClassLoader]

それで、パフォーマンスは良くなったの?

マシンスペックなどにも大きく依存するでしょうし、オラクル製品はベンチマークを公開してはいけないポリシーだったはずなので、詳細は記載しませんが、今回の私の環境では、WLS起動時のパフォーマンスはかなり向上しました。
また、今回はWLS管理サーバだけで確認しましたが、1つのホスト・ドメイン内で複数のWLSインスタンスを起動する際には、メモリフットプリントも改善されることが期待できます。

まとめ

それではまとめです。

  • WLS 12.2.1にはドキュメント未記載ですが、アプリケーションクラスデータ共有(AppCDS)を利用する機能が組み込まれています。
  • AppCDSを利用すると、WLSの起動パフォーマンスの改善や、メモリフットプリントの向上が期待できます。
  • 正式にリリースされた際には、ライセンスに注意しつつ検証して利用しましょう。

*1:マルチテナントなどは商用で使うにはWLS Enterprise Editionやオプションライセンスが必要となり、個人的には勉強会では取り上げにくいので省きました。

*2:10/19勉強会の夜間にちょうどリリースされたため、先のスライドには含められませんでしたが、このパッチセットは基本的にBug修正がメインのようなので、それほど目立った新機能は見当たりません。

*3:開発環境用としてのみ動作保証されているようです。

*4:同様のクラス共有機能はIBM JDKでも実装されています。

*5:つまり本番環境などで利用するにはライセンスが必要です。

*6:直接は関係ない「-showversion」も入ってます。

*7:もちろんJava SEの標準APIも共有アーカイブから読み込まれるようになります。

WebLogic Server 12.2.1のThreadLocal Clean Out機能を検証してみる #javaee #jpoug

本日はJava EE Advent CalendarJPOUG Advent Calndarの12日目です。*1

WebLogic Server(以降、WLS) 12.2.1が先日10/26にリリースされました。
これは、12c Release 2(12cR2)であり、およそ3年ぶりのメジャーバージョンアップの位置付けとなっています。
目玉となる新機能としては以下があり、公式からも様々な情報が順次出てきています。

  • Java EE 7 Full Support*2
  • Multi Tenancy Support
  • Continuous Availability

ここでは、細かいですが痒い所に手が届く新機能として、ThreadLocal Clean Outを紹介・検証してみます。

ThreadLocalとクラスローダリークのおさらい

まず、WLSに限らずJavaアプリケーションサーバ全般として、ThreadLocal利用時の注意点を一昨年のAdvent Calendarにて記事を書きました。
基本的にはThreadLocalを使った場合は、確実に破棄しないとクラスローダリークの原因になります。*3
また、記事にも少々記載していましたが、当時TomcatではThreadLocalの自動削除に対応しており、WLSでも同様の機能があるとよいなと思っていたので*4、WLS 12.2.1からの新機能として実装されたのは、そのときの意見が反映されたようにも感じ、嬉しいところです。

ThreadLocalを使ったサンプルアプリケーション

ThreadLocalはフレームワークやライブラリ内で利用されることも多いと想定され、その場合は確実に破棄したり、破棄メソッドを別途呼び出すようなガイドラインもあるでしょう。
ここではスレッドセーフでなくて、現場でトラブルが起きそうな代表格の一つ*5であるSimpleDateFormatを使った実装を考えてみます。

もちろん、JDK 8からはDate and Time APIDateTimeFormatterが用意されており、スレッドセーフなのでこれを使うべきでしょうが、レガシーだったり安易な実装であると考えてください。

シナリオ

SimpleDateFormatを継承したクラスを利用していたが、スレッドセーフではなく、テスト時に問題が発覚した*6ために、ThreadLocalを使った実装に切り替えたというシナリオで考えてみましょう。
以下のようなサンプルとしてみました。*7

public class CustomDateFormat extends SimpleDateFormat {
    public CustomDateFormat() {
        super("yyyy-MM-dd HH:mm:ss.SSS");
    }    
}
@ApplicationScoped
@Path("now")
public class NowResource {

    private static final ThreadLocal<DateFormat> DF_LOCAL
            = ThreadLocal.withInitial(() -> new CustomDateFormat());

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String getText() {
        DateFormat df = DF_LOCAL.get();
        
        System.out.printf("Thread: %s%n", Thread.currentThread().getName());
        System.out.printf("this identity=0x%x%n", System.identityHashCode(this));
        System.out.printf("DF_LOCAL identity=0x%x%n", System.identityHashCode(DF_LOCAL));
        System.out.printf("df identity=0x%x%n", System.identityHashCode(df));
        System.out.println();

        return df.format(new Date());
    }
}
サンプルアプリケーションの考慮点・注意点

それほど本質ではないので、飛ばしていただいて構いませんが、念のため以下について、ご留意・ご了承ください。

  • package/import文は、ここでは省略しています。
  • SimpleDateFormatを継承した単純なCustomDateFormatクラスをあえて作成していますが、アプリケーション内で共通化した日付書式を使いたかったシナリオとご理解ください。
    • SimpleDateFormatは、ブートストラップクラスローダにて通常はロードされるため、そのままではThreadLocalに入れてもクラスローダリークは発生しないはずです。
    • CustomDateFormatは、Javaアプリケーションサーバが実装しているWebアプリケーション用のクラスローダ(WLSではweblogic.utils.classloaders.ChangeAwareClassLoader)にてロードされるクラスとしています。
  • NowResourceはJAX-RSのリソースクラスとしておき、都度インスタンス化される必要もないため、CDIの@ApplicationScopedを組み合わせています。
    • javax.ws.rs.core.Applicationを継承したクラスも必要ですが、ここでは省略しています。
    • @ApplicationScopedであるため、ThreadLocalの宣言はstaticでなくてもよいですが、スレッドに紐付けて管理されるためstaticで定義することが比較的多いかと思います。
    • JDK 8から導入されたThreadLocal.withInitialを使って微妙にラムダ式を使っていますが、それほど意味はありません。後述しますが、従来から利用していたであろうinitialValue()をオーバーライドしてもよいでしょう。
    • System.outは本番用アプリケーションでは使うべきではないですが、あくまでデバッグ目的の検証用・サンプルということでご理解ください。

実行してみる

上記のサンプルアプリケーションをWLS 12.2.1にデプロイし、実行してみましょう。
ブラウザや任意のHTTPクライアントで「http://host:port/context-root/application-path/now」にアクセスしてみてください。*8
何度か実行すると、以下のような形で標準出力に出力されました。

Thread: [ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'
this identity=0x52097801
DF_LOCAL identity=0x5bc771ce
df identity=0x2f93ea4c

Thread: [ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'
this identity=0x52097801
DF_LOCAL identity=0x5bc771ce
df identity=0x7b243b31

Thread: [ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'
this identity=0x52097801
DF_LOCAL identity=0x5bc771ce
df identity=0x7b243b31

Thread: [ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'
this identity=0x52097801
DF_LOCAL identity=0x5bc771ce
df identity=0x2f93ea4c

thisやDF_LOCALは、当たり前ですが何度実行してもオブジェクトIDに変化はありません。
dfのところに注目すると、WLSの実行スレッド(上記であればExecuteThread: '0'と'2')ごとにオブジェクトIDが異なるのが確認できます。
言い換えると同じスレッドであれば、CustomDateFormatが再利用されているのが分かります。

  • ExecuteThread: '0' → 0x2f93ea4c
  • ExecuteThread: '2' → 0x5bc771ce

これでめでたしめでたし!という訳には残念ながら行きません。
ThreadLocal#remove()を呼び出す箇所もないため、再デプロイすることで、基本的にクラスローダリークにつながるはずです。

EagerThreadLocalCleanupを有効にする

ようやく本題のところに入っていきましょう。ThreadLocal Clean Outのドキュメントを確認すると、KernelMBeanにeagerThreadLocalCleanup属性があり、これを有効化すれば機能しそうです。

To clean up stray ThreadLocal use by applications and third-party libraries, configure the eagerThreadLocalCleanup attribute in the KernelMBean. The eagerThreadLocalCleanup attribute specifies whether to clean up all ThreadLocal storage from self-tuning thread pools after they have finished processing each work request.

早速有効化してみたいところですが、管理コンソールにはそれらしき設定は見当たりません。こんなときはWLST(WebLogic Scripting Tool)を使うのが定石です。WLSTの使い方はここでは説明しませんので、WLS勉強会の資料「使ってみよう WLST」などを参照してください。

まず、connectコマンドで接続して、findコマンドで属性がどこに定義されているかを探してみましょう。

wls:/offline> connect('weblogic','welcome1')
ユーザーID weblogicでt3://localhost:7001に接続しています ...
ドメイン"base_domain"に属する管理サーバー"AdminServer"に。が正常に接続されました

警告: サーバーへの接続に安全でないプロトコルが使用されました。
通信セキュリティを確保するには、かわりにSSLポートまたは管理ポートを使用する必要があります。

wls:/base_domain/serverConfig/> find('threadlocal')
すべての登録済MBeanインスタンスで"threadlocal"を検索しています...
/Servers/AdminServer                           EagerThreadLocalCleanup                            false

ここではAdminServerに定義されていることが分かりますが、複数のWLSインスタンスがあれば、それぞれ設定することになります。
なお、ドキュメントではKernelMBeanにeagerThreadLocalCleanup属性があることになっていますが、個々のWLSインスタンスはServerMBeanとして参照・設定します。そして、ServerMBeanはKernelMBeanを継承していることが、MBeanリファレンスAPIドキュメントからも分かります。

それではEagerThreadLocalCleanupを有効化してみましょう。設定変更後にWLSの再起動が必要となります。

wls:/base_domain/serverConfig/> edit()

wls:/base_domain/edit/> startEdit()
編集セッションを開始しています ...
編集セッションが開始されました。変更が完了したら、必ず保存してアクティブ化してください。
wls:/base_domain/edit/ !> cd('/Servers/AdminServer')
wls:/base_domain/edit/Servers/AdminServer !> cmo.setEagerThreadLocalCleanup(true)
wls:/base_domain/edit/Servers/AdminServer !> save()
すべての変更を保存しています ...
すべての変更が正常に保存されました。
wls:/base_domain/edit/Servers/AdminServer !> validate()
変更を検証しています ...
変更が正常に検証されました
wls:/base_domain/edit/Servers/AdminServer !> showChanges()

アクティブ化されていないすべての変更:

変更されたMBean: com.bea:Name=AdminServer,Type=Server
呼び出された操作: modify
変更された属性: EagerThreadLocalCleanup
属性の古い値: false
属性の新しい値: true
サーバーの再起動が必要: true

wls:/base_domain/edit/Servers/AdminServer !> activate()
すべての変更をアクティブ化しています。しばらく時間がかかる場合があります ...
アクティブ化が完了すると、この編集セッションに関連付けられた編集ロックが開放されます。

MBeanで次の静的属性が変更されたため、
serverの再起動が必要です。
変更されたMBean: com.bea:Name=AdminServer,Type=Server
変更された属性: EagerThreadLocalCleanup

アクティブ化が完了しました

再度実行してみる

WLSの再起動が完了したら、再びアプリケーションを何度か実行してみましょう。
以下のような出力が確認できます。

Thread: [ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'
this identity=0x142db2b2
DF_LOCAL identity=0x7b62b3d4
df identity=0x78301aea

Thread: [ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'
this identity=0x142db2b2
DF_LOCAL identity=0x7b62b3d4
df identity=0x25cba42c

Thread: [ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'
this identity=0x142db2b2
DF_LOCAL identity=0x7b62b3d4
df identity=0x58d69058

Thread: [ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'
this identity=0x142db2b2
DF_LOCAL identity=0x7b62b3d4
df identity=0x38578023

thisとDF_LOCALは、(こちらも当たり前ですが)先ほどと同様に何度実行してもオブジェクトIDに変化はありません。
しかし、dfは同じ実行スレッド(ここでは、"ExecuteThread: '0'")でも、毎回オブジェクトIDが異なっており、リクエストの都度オブジェクトが再作成されている、つまりThreadLocal領域が破棄されていることが窺えます。

生成・破棄の動作を確認する

先のサンプルアプリケーションのThreadLocalの宣言を一部変更し、以下のようにします。*9

    private static final ThreadLocal<DateFormat> DF_LOCAL = new ThreadLocal<DateFormat>() {
        @Override
        protected DateFormat initialValue() {
            System.out.println("Initialized!");
            return new CustomDateFormat();
        }

        @Override
        public void remove() {
            super.remove();
            System.out.println("Removed!!");
        }
    };

これでビルド・デプロイし、再度アプリケーションを何度か実行すると以下のようになりました。

Initialized!
Thread: [ACTIVE] ExecuteThread: '4' for queue: 'weblogic.kernel.Default (self-tuning)'
this identity=0x4276fc09
DF_LOCAL identity=0x771e79d2
df identity=0x49683253

Initialized!
Thread: [ACTIVE] ExecuteThread: '4' for queue: 'weblogic.kernel.Default (self-tuning)'
this identity=0x4276fc09
DF_LOCAL identity=0x771e79d2
df identity=0x387e8df1

Initialized!
Thread: [ACTIVE] ExecuteThread: '4' for queue: 'weblogic.kernel.Default (self-tuning)'
this identity=0x4276fc09
DF_LOCAL identity=0x771e79d2
df identity=0x5c5ffe39

Initialized!
Thread: [ACTIVE] ExecuteThread: '4' for queue: 'weblogic.kernel.Default (self-tuning)'
this identity=0x4276fc09
DF_LOCAL identity=0x771e79d2
df identity=0x43972404

この結果から、initialValue()はリクエストの都度呼ばれているため、再作成されていることは分かりますが、remove()による明示的な破棄が行われていません。
WLSはオープンソースではないため、どのような実装となっているか分かりませんが、EagerThreadLocalCleanup属性を有効化しても、ThreadLocal#remove()を呼びだすのではなく、別の方法を使って実行スレッドのThreadLocal領域を破棄しているようです。

remove()が呼ばれなくても、以前にThreadLocalに紐付いていたオブジェクトは参照から外れ、通常はGC対象となるため、問題になるケースは少ないでしょう。
ThreadLocalに外部リソースを管理するクラス*10が紐づくような実装をしている場合は、注意が必要かもしれません。もっとも、その場合は自動削除に頼るのではなく、アプリケーションで明示的にThreadLocal#remove()を呼ばないと、リソースが枯渇して問題になるケースが多いはずです。

考察してみる

上記までの動作から、EagerThreadLocalCleanupの設定を常に有効にしたほうがよいのでしょうか?

今回のサンプルアプリケーションの例では、DateFormatオブジェクトをキャッシュして効率的に扱うためにThreadLocalに紐付けていました。リクエストの都度、オブジェクトが再生成されてしまうのでは、ThreadLocalを利用してスレッドセーフに扱えるよう工夫した意味もなくなってしまいます。

とはいえ、インフラ観点では、再デプロイ時にクラスローダリークが発生してしまっては、長期間安定的に運用できず、再起動による運用の負荷も上がってしまうのではないか……


ご安心ください。WLS 12.2.1のThreadLocal Clean Outのドキュメントには続きがあります。

By default, the eagerThreadLocalCleanup attribute is set to false, in which the self-tuning thread pool only cleans up ThreadLocal storage when a thread returns to a standby pool and after an application is undeployed.

Setting the eagerThreadLocalCleanup attribute to true ensures that all thread pool threads have no leftover ThreadLocal values from previous requests when running work for a new request. However, overhead occurs from cleaning up ThreadLocal storage after each work request and then reestablishing ThreadLocal values for each new request. Since some applications cache objects that are expensive to create in the ThreadLocal storage, cleaning up ThreadLocal values after each request may negatively impact performance on those applications.

つまり、EagerThreadLocalCleanupがデフォルトの無効のままであっても、アンデプロイ後にはThreadLocal領域は破棄されるとのことです。
また、EagerThreadLocalCleanupを有効にしていると、リクエストの都度にThreadLocal領域が破棄されるので、アプリケーションによってはパフォーマンスに悪影響を与える可能性についても言及されています。

クラスローダリークを検証してみる

では、念のため、同じアプリケーションをWLS 12.1.3/WLS 12.2.1で何度か再デプロイ・実行して、ヒープダンプを見てみましょう。ここではVisualVMを使ってみることにします。*11

WLS 12.1.3の場合

WLSのプロセスを右クリックしてヒープダンプを取得し、[クラス]ビューから「CustomDateFormat」でフィルタしてみます。同じクラスが複数ロードされており、クラスローダリークが発生していることが窺えます。

WLS 12.2.1の場合

同じようにWLS 12.2.1で、EagerThreadLocalCleanupは無効にしたままやってみます。CustomDateFormatが一つだけになっており、クラスローダリークは発生していなさそうなのが分かりますね。

もう少し補足

TomcatのThreadLocal自動削除機能は、アンデプロイ時にスレッドプールを破棄するという、半ば力技的な実装にも見受けられます。
これと比較して、WLS 12.2.1のThreadLocal Clean Outは、この記事を書くにあたって調べてみた限りでは、コア機能である実行スレッドプールを活かしたまま、ThreadLocal領域を破棄してくれるので、かなり実運用に耐えられる感があります。

ただし、クラスローダリークはThreadLocalだけが原因ではありません。
JDK 8以降ではPermanent領域がなくなり、代わりにネイティブ領域にMetaspaceとして確保されることになりましたが、デフォルトではMetaspaceの最大サイズは、ほぼ無制限となっています。*12
ThreadLocal以外のリークも考えられますので、運用時はMetaspaceの領域を監視したり、-XX:MaxMetaspaceSizeオプションを利用して、Metaspace領域のサイズを制限したほうが、プロセスの肥大化防止につながるので安全でしょう。

まとめ

それでは、まとめです。

  • WLS 12.2.1はThreadLocal Clean Out機能があり、アプリケーションでThreadLocalを破棄し忘れても、クラスローダリークが発生する可能性が低くなりました。
  • この機能はほとんどの場合、有効に動作するはずですが、それに頼ることなく、ThreadLocal#remove()を明示的に呼び出した方が安全でしょう。
  • ThreadLocal以外が原因で、クラスローダリークが発生する可能性はありますので、Permanent/Metaspace領域のサイズを監視・制限することも検討しましょう。

*1:どちらも被せてしまいましたがお許しください…

*2:前リリースであるWLS 12.1.3でも部分的にJava EE 7に対応していました

*3:その後に、id:n_agetsumaさんによる記事第十回 #渋谷javaでの資料も出て、非常に分かりやすいです

*4:それはそれでid:nekopさんからツッコミが入ったりしていた

*5:スレッドセーフでなくて現場でトラブルが発生する代表格の本丸はjava.util.HashMapですかね…

*6:実際に私も同じようなことをやらかしたことがあります…

*7:無理矢理感があるので、マサカリ投げどころ満載だと思いますが…

*8:RESTful APIとしては、現在の日時が返るだけですので、特に面白いところはありません…

*9:本質に関係ないのですが、匿名内部クラスだとダイヤモンド演算子が使えないのを今更知りました…

*10:ファイルやDBコネクションなど、一般的に明示的なclose()による破棄をしないとリソースリークが発生する

*11:先に紹介したn_agetsumaさんの記事ではEclipse Memory Analyzer(MAT)を使っており、こちらのほうが詳細な分析はできると思います

*12:この辺りの詳細は、末永さんの資料や、「Java8のHotSpotVMからPermanent領域が消えた理由とその影響」の記事が参考になります

WebLogic ServerのExalogic最適化機能を調べてみる #JPOUG

この記事はJPOUG Advent Calendarの23日目です。

最近のOracle OpenWorldでは毎年アプライアンス製品*1が発表されている訳です。
その中でミドルウェアに特化した製品としては、Oracle Exalogic Elasitic Cloud(以降、基本的にExalogicと略)が数年前に発表されました。
この記事ではWebLogic Server(以降、基本的にWLSと略)のExalogic最適化機能について調べてみましょう。*2
当然ながら、通常のWLSでは利用できず、Exalogicのハードウェアおよびソフトウェアライセンスで利用できる機能ではあるのですが、WLSのチューニングの方向性について、何かしらヒントが得られるかもしれません。

マスタースイッチ

当然手元にExalogicがある訳はないので、擬似的に以下の環境で見てみたいと思います。

さて、WLSにはExalogic用のいわゆる“マスタースイッチ”と呼ばれる設定があります。

  • {ドメイン}→構成→一般→Exalogic最適化の有効化

この設定をOnにして、再起動することで、WLSのExalogic最適化機能が有効化されるのですが、実際にはどのような機能があるのでしょう。
WLSの本番モードだと、管理コンソール左上のチェンジセンターから、[変更と再起動の表示]にて、変更される内容を事前に確認することができます。

MBeanレベルで様々な設定が有効になることが分かりますね。
余談も含みますが、運用時に[変更のアクティブ化]をする前には、必ず[変更と再起動の表示]にて、意図する設定のみ反映される(逆に言うと、意図しない設定が反映されない)ことを確認した方がよいです。
WLST(WebLogic Scripting Tool)であれば、編集中に showChanges() を実行することで同様の情報を確認できます。

wls:/wls1213-exalogic/edit !> showChanges()

All changes that are made but not yet activated are:

MBean Changed : com.bea:Name=wls1213-exalogic,Type=Domain
Operation Invoked : modify
Attribute Modified : ExalogicOptimizationsEnabled
Attributes Old Value : false
Attributes New Value : true
Server Restart Required : true


MBean Changed : com.bea:Name=Cluster-0,Type=Cluster
Operation Invoked : modify
Attribute Modified : SessionLazyDeserializationEnabled
Attributes Old Value : false
Attributes New Value : true
Server Restart Required : true


MBean Changed : com.bea:Name=AdminServer,Type=Server
Operation Invoked : modify
Attribute Modified : MuxerClass
Attributes Old Value : weblogic.socket.NIOSocketMuxer
Attributes New Value : weblogic.socket.NIOSharedWorkSocketMuxer
Server Restart Required : true


MBean Changed : com.bea:Name=AdminServer,Type=Server
Operation Invoked : modify
Attribute Modified : AddWorkManagerThreadsByCpuCount
Attributes Old Value : false
Attributes New Value : true
Server Restart Required : true


MBean Changed : com.bea:Name=AdminServer,Type=Server
Operation Invoked : modify
Attribute Modified : GatheredWritesEnabled
Attributes Old Value : false
Attributes New Value : true
Server Restart Required : true


MBean Changed : com.bea:Name=AdminServer,Type=Server
Operation Invoked : modify
Attribute Modified : ScatteredReadsEnabled
Attributes Old Value : false
Attributes New Value : true
Server Restart Required : true


MBean Changed : com.bea:Name=AdminServer,Type=Server
Operation Invoked : modify
Attribute Modified : UseConcurrentQueueForRequestManager
Attributes Old Value : false
Attributes New Value : true
Server Restart Required : true


MBean Changed : com.bea:Name=AdminServer,Type=TransactionLogJDBCStore,Server=AdminServer
Operation Invoked : modify
Attribute Modified : OraclePiggybackCommitEnabled
Attributes Old Value : false
Attributes New Value : true
Server Restart Required : true

(以降、管理対象サーバー分も同様なので省略)

ちょっとまとめてみましょう。以下のような設定があります。

Type Attribute Value
Domain ExalogicOptimizationsEnabled true
Cluster SessionLazyDeserializationEnabled true
Server MuxerClass weblogic.socket.NIOSharedWorkSocketMuxer
Server AddWorkManagerThreadsByCpuCount true
Server GatheredWritesEnabled true
Server ScatteredReadsEnabled true
Server UseConcurrentQueueForRequestManager true
TransactionLogJDBCStore OraclePiggybackCommitEnabled true

これらの設定の詳細はドキュメントで確認することができます。

なお、{WLSドメイン}/config/config.xmlを確認すると、“マスタースイッチ”であるExalogicOptimizationsEnabledについては反映されますが、他の項目については直接反映されていないことが分かります。

  <exalogic-optimizations-enabled>true</exalogic-optimizations-enabled>

個別の設定に関しては、必要に応じてオーバーライドでき、その時点でconfig.xmlにも反映されます。
例えばSessionLazyDeserializationEnabledは、HTTPセッションの遅延デシリアライズを有効にすることで、レプリケーション時のメモリ利用を効率化できるようですが、おそらくパフォーマンスとしては無効化しておいた方がよいケースも考えられます。

動作の確認

少しだけ実際の動作がどうなっているか確認してみましょう。
前回の記事と同様にスレッドダンプから見ることにします。

"ExecuteThread: '3' for queue: 'weblogic.socket.Muxer'" #40 daemon prio=5 os_prio=31 tid=0x00007ff1cb684000 nid=0x7103 runnable [0x000000011eb26000]
   java.lang.Thread.State: RUNNABLE
	at sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method)
	at sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:198)
	at sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:103)
	at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
	- locked <0x00000007a1b0aed0> (a sun.nio.ch.Util$2)
	- locked <0x00000007a1b0aec0> (a java.util.Collections$UnmodifiableSet)
	- locked <0x00000007a1b0ad90> (a sun.nio.ch.KQueueSelectorImpl)
	at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
	at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:101)
	at weblogic.socket.NIOSocketMuxer.selectFrom(NIOSocketMuxer.java:529)
	at weblogic.socket.NIOSharedWorkSocketMuxer.processSockets(NIOSharedWorkSocketMuxer.java:92)
	at weblogic.socket.SocketReaderRequest.run(SocketReaderRequest.java:30)
	at weblogic.socket.SocketReaderRequest.execute(SocketReaderRequest.java:43)
	at weblogic.kernel.ExecuteThread.execute(ExecuteThread.java:147)
	at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:119)

NIOSharedWorkSocketMuxer が使われていることが分かりますね。

また、以下のようなスレッドも確認できます。

"RequestManagerPoller" #20 daemon prio=10 os_prio=31 tid=0x00007ff1c8cd2000 nid=0x3507 waiting on condition [0x000000011cc38000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:304)
	at weblogic.utils.collections.Turnstile.awaitOn(Turnstile.java:79)
	at weblogic.utils.collections.Turnstile.await(Turnstile.java:100)
	at weblogic.utils.collections.Turnstile.spinAwait(Turnstile.java:93)
	at weblogic.utils.collections.PartialOrderSet$WindowedArrayQ.dequeue(PartialOrderSet.java:757)
	at weblogic.utils.collections.PartialOrderSet.take(PartialOrderSet.java:671)
	at weblogic.work.RequestManager$BufferQueueDrainer.run(RequestManager.java:1336)
	at java.lang.Thread.run(Thread.java:745)

これはUseConcurrentQueueForRequestManagerの設定によって反映されるようです。

他にも見て行きたいところですが、実機でパフォーマンス含め確認しないと意味ないものもありますし、長くなりすぎるのでこの辺りにしておきましょう。*4

他のExalogic最適化機能

先日、12/18にWebLogic Server勉強会で発表した資料で少しだけ触れているのですが、WLS 12.1.3では以下の機能がExalogic用に追加されていて、起動・停止スクリプトからその一端を垣間見ることができます。

まとめ

  • WLSのExalogic最適化機能は、必ずしもExalogic環境でなくても擬似的に確認できるものもあります。*5
  • 一足早いですが、メリークリスマス!とは言え、サンタさん、自宅にExalogicは要りません。電源が吹っ飛びます。というか床が抜けます。

*1:正確にはOracle Engineered Systemsと呼ぶ。

*2:細かいですが間違いやすいこととして、Web"L"ogicとExa"l"ogicで、大文字小文字が異なります!

*3:WLS 12.1.3はOracle JDK 8u20以降で動作保証していますが、Fork/Join Frameworkやパラレルストリームはサポートしていません。また、Mac OS X 10.10は記事執筆時点では正式動作保証はされていません。

*4:単に疲れただけとも言う…

*5:もちろんSDP(Socket Direct Protocol)のようにハードウェア依存(InfiniBand)のところは、実機でないと厳しいです。