ShowTable of Contents
Lotus Notes Java クラスでは、開発者が明示的にリサイクルをして使用したオブジェクトを開放する必要があります。
XPages のサーバーサイド JavaScript (SSJS) でもその処理の裏側では Java の動いており、これは当てはまります。不要になったオブジェクトは Java のガーベージコレクションで開放されますが、Lotus Notes クラスのオブジェクトは Java クラスのオブジェクトの中で、Lotus Notes エンジンの C++ コードが呼ばれているため Java のガーベジレコクションの仕組みではオブジェクトの開放ができません。開発者が明示的にC++ コードで生成された Lotus Notes クラスのオブジェクトのの開放を行う必要があります。作成したクラスに対して recycle() メドッドを呼ぶことで、不要になったクラスを解放してSSJS で使用する Lotus Notes クラスを開放します。
それでは、このリサイクルを怠ることでシステムにどれほどの負荷がかかるでしょうか?
リサイクルを怠ってメモリリークを引き起こすアプリケーションを実際に作成しメモリリークの発生を検証しました。その結果を通してリサイクルの必要性について考察します。
データ保持用の Lotus Notes アプリケーション
検証には以下のようなフォームとビューが1つづつある簡単な Lotus Notes アプリケーション(NSF ファイル)を使用します。
このフォームには以下のフィールドがあります。
-
FirsName フィールド: テキスト
-
LastName フィールド: テキスト
「Contact」フォームには姓(FirstName)と名(LastName)など連絡先情報を保持させます。「AllContacts」ビューは「Contact」フォームの文書を一覧で表示します。検証のために1万文書を作成しました。
※ このサンプルを作るために OpenNTF.org の XPages Extension Library に含まれる XPagesEXT.nsf を利用しました。
データ処理用の Lotus Notes アプリケーション
このサンプルデータを処理する XPages アプリケーションを、異なる Lotus Notes アプリケーション(NSF ファイル)上で作成します。NSF ファイルを分けるのは、NotesDataabse クラスのインスタンスなどの生成やリサイクルを明示的に行うためです。
XPage を1つ作成し、その「beforeRenderResponse」イベントに以下のようなスクリプトを実装します。このスクリプトは以下のような処理を行います。
-
URL パラメータから「start」と「offset」の値を取得します。これは1万個ある文書のどこから何個の文書を処理するかを指定できます。たとえば start=1 と offset=1000 で最初の文書から千文書を処理します。
-
データ保持用の NSF ファイルを開き、「AllContacts」ビューの全文書を NotesDocumentCollection クラスのインスタンスとして取得します。
-
start で指定された文書を開き、「FirstName」と「LastName」アイテムの値を取得します。
-
同様にコレクションの次の文書を開き、「FirstName」と「LastName」アイテムの値を取得します。 これを offset に指定された回数だけ繰り返します。
-
最後に処理結果をビュースコープ変数に格納して終わります。XPage には計算結果フィールドコントロールを配置してこの処理結果を表示させます。
-
以下のスクリプトでは、ループ内やスクリプトの最後で Lotus Notes クラスのインスタンスのリサイクルを適切に行っています。
しかし、このリサイクル処理を意図的に削除した XPage も作成し、同じ処理を行った結果を比較します。
viewScope.appTitle = "RECYCLE TEST ";
// URL パラメータからテストデータ取得
var start = Number(param.get("start"));
var offset = Number(param.get("offset"));
print (viewScope.appTitle + "start = " + start + ", offset = " + offset);
// データを取得する NSF の定義
var serverName = @Name("[CN]",session.getServerName());
var dbPath = "SampleDemo.nsf";
var viewName = "AllContacts";
// ビューの文書を NotesDocumentCollection に取得
var db:NotesDatabase = session.getDatabase(serverName,dbPath);
var vw:NotesView = db.getView(viewName);
var docCol:NotesDocumentCollection = vw.getAllDocumentsByKey("");
// 最初の文書を取得
var doc:NotesDocument = docCol.getNthDocument(start);
// 作業用変数
var tempDoc:NotesDocument;
var item1:NotesItem;
var item2:NotesItem;
var name = "*** no item ***";
// コレクションで文書でループ
for (i=0; i<offset; i++) {
// 文書が取得できなければ中止
if (null == doc) {
print (viewScope.appTitle + "Encounter null document at offset " + i);
break;
}
// 文書のアイテムデータ取得
item1 = doc.getFirstItem("FirstName");
item2 = doc.getFirstItem("LastName");
name = item1.getText() + " " + item2.getText();
// 次の文書の取得
tempDoc = docCol.getNextDocument(doc);
// リサイクル
item1.recycle();
item2.recycle();
doc.recycle();
doc = tempDoc;
}
// リサイクル
vw.recycle();
db.recycle();
// 結果の出力とスコープ変数への設定
print (viewScope.appTitle + "name = " + name);
viewScope.contactName = name;
本記事では XPages アプリケーション内の SSJS でのメモリリークを検証しています。メモリリークの状況を見るために、Lotus Domino と共にインストールされる障害解析用のツールである NSD を使用しました。NSD が出力したファイル (NSD ログ) は、Lotus Domino サーバーの障害を解析する際の資料として使用でき、仮想メモリの情報なども含んでいます。
この NSD ログには、Lotus Domino のタスクのメモリ使用量の情報も含んでいます。たとえば HTTP タスクのメモリ使用量の概要は以下のように表示されます。
<@@ ------ System Data -> Performance Data -> Process Memory Mappings (summary) :: [ nHTTP: 0df8] (Time 11:55:45) ------ @@>
COMMIT RESERVED TOTAL WS SIZE WS SHARED
PRIVATE: 94.0M 323.7M 417.7M 74.6M 4.0K
MAPPED: 97.1M 3.9M 101.0M 6.1M 5.4M
IMAGE: 84.4M 0.0K 84.4M 25.8M 10.5M
TOTAL: 275.5M 327.6M 603.1M 106.5M 15.9M
Totals: Used=603.1M, Free= 1.4G, Fragmented=22.5M, Overall= 2.0G, maxFree= 0.0K, Limit=7ffeffff ( 2.0G)
この検証には、NSD ログの HTTP タスクのメモリ使用量の概要から、プライベートメモリの値を見ることとします。
この検証に当たり XPages アプリケーションへの繰り返し処理が必要になります。手動で行ってもかまいませんが、正確なテスト操作を行うためにテスト自動化ツールを使用するのが良いでしょう。有名なパフォーマンステストツールである Apache JMeter を XPages アプリケーションで使う方法について、以下の記事にまとめられています。
また、NSD ログの取得も自動化するために、コンソールコマンドとして「load nsd」を投入する XPages アプリケーションを作成すると、JMeter でのテスト操作と統合できて便利でしょう。LotusScript や Java/JavaScript Lotus Notes クラスでコンソールコマンドを投入する方法については、以下のブログに記載されています。
この検証は以下の環境で行いました。
2CPUでメモリ4GBのバーチャルマシン
Windows XP
Lotus Domino 8.5.3 FP3 (32Bit)
1万文書を処理した結果
先のテスト用2つの XPages で1万文書を処理させて(start=1 と offset=10000 を指定)ページを表示させてみました。ふたつのページの違いは「beforeRenderResponse」イベントのスクリプト内で Lotus Notes クラスのリサイクルを行っているかどうかだけです。
それぞれのテスト前に Lotus Domino を再起動し HTTP タスクの起動を待って NSD ログを取得し、さらに1回ページを表示させるごとに NSD ログを取得する操作を5回繰り返しました。
図 リサイクル処理を行っている XPage の表示でのメモリ消費
リサイクル処理を行っているページの表示では、最初の表示のためにいくらかのメモリが消費されながらも、そのメモリは開放されています。そのため Commit のメモリの増加はなく、Reserved のメモリが増加しています。
図 リサイクル処理を行ってない XPage の表示でのメモリ消費
リサイクル処理を行っていないページの表示では、リサイクルをしているページと同じ Reserved のメモリが増加に加え、開放されていいない Commit のメモリが増加しています。これはスクリプトで Lotus Notes クラスのリサイクルを行っていないことによるメモリリークと考えられます。
このテストではページの開示後に10秒、NSD コマンド発行後に90秒の待ち時間を入れています。JMeter でリサイクルをしていないページにさらに高い負荷(1秒ごとのページリクエストを連続100回)をかけたところ、簡単に Out Of Memory エラーが発生しました。
次にリサイクルを怠っている XPage で処理対象の文書を徐々に増やしてページを表示させ、メモリの使用量を確認しました。start=1 のままで offset の値を1,000から10,000へと1,000づつ増加をさせ、ページ表示後に NSD ログの取得を行いました。
図 処理対象の Domino 文書数とメモリ消費
処理文書の増加に応じてメモリの消費が増えていることがわかります。ページ表示後に処理した Domino 文書(NotesDocument クラスのオブジェクトイ)がリサイクルされず、処理する文書数に応じてその量が増えていることを示しています。
ここまでの検証結果より、Lotus Notes Java クラスのリサイクルが適切に行われないことでメモリの消費が増大して、システムに悪影響が及ぶことが確認できました。この問題は開発中の小規模な利用では発見されにくく、アプリケーションがサービスインして利用者が増えてくるに従い判明してくる問題です。
XPages アプリケーションの開発においては、開発段階でオブジェクトのリサイクルに留意する必要があります。