ガーベッジ・コレクション (GC) は、アプリケーションがヒープ・メモリーに新規オブジェクトの割り振りを継続して行えるように、未使用の Java ヒープ・メモリーを回収するため、Java 仮想マシン (JVM) の不可欠な構成要素です。GC の有効性とパフォーマンスは、アプリケーションのパフォーマンスとその決定性で重要な役割を果たします。(サポート対象のプラットフォーム上で稼動する) IBM WebSphere Application Server V8 に付属の IBM JVM には、次の 4 種類の GC ポリシー・アルゴリズムが用意されています。
-Xgcpolicy:optthruput-Xgcpolicy:optavgpause-Xgcpolicy:gencon-Xgcpolicy:balanced
これらのアルゴリズムは、パフォーマンスと決定性の品質がそれぞれ異なります。また、WebSphere Application Server V8 のデフォルト・ポリシーは、-Xgcpolicy:optthruput ポリシーから -Xgcpolicy:gencon ポリシーに変更されています。ここでは、それぞれのポリシーと共に、デフォルト・ポリシーのこの変更が何を意味するかを見ていきます。
本来、メモリーの使用パターンはアプリケーションごとに異なります。計算集約型の複雑な計算ワークロードでは、きわめてトランザクショナルな顧客向けインターフェースと同じように Java ヒープが使用されるわけではありません。このように多彩なワークロードを最適に処理するには、さまざまなガーベッジ・コレクション・ポリシーが必要です。IBM JVM では多種類のガーベッジ・コレクション・ポリシーをサポートしているため、アプリケーションに最適なポリシーを選択できます。
パラレル mark-sweep-compact コレクター: optthruput
実現可能な範囲で最もシンプルなガーベッジ・コレクション手法は、空きメモリーがすべて使用されるまで割り振りを続け、空きメモリーがなくなるとアプリケーションを停止して、ヒープ全体を処理するというものです。これは非常に効率的なガーベッジ・コレクターとなりますが、コレクターによって生じる一時停止をユーザー・プログラムが許容できる必要があります。この方針は、全体のスループットのみが問題となるワークロードで効果的です。
optthruput ポリシー (-Xgcpolicy:optthruput) には、この方針が実装されています (図1)。このコレクターではパラレル mark-sweep アルゴリズムが使用されます。つまり、このコレクターはまず、到達可能なオブジェクトを調べて、ライブ・データとしてマークします。続く過程では、マークされていないオブジェクトをスイープして、新たな割り振りに使用できるように空きメモリーを確保します。この作業の大部分を並列で行うことができるため、このコレクターでは、追加スレッド (デフォルトでは CPU の数以下) を使用してより迅速に作業を完了させ、アプリケーションが停止したままになる時間を短縮します。
図1. アプリケーションとコレクターの CPU 使用状況:optthruput
mark-sweep アルゴリズムの問題は、フラグメント化につながる可能性があるという点です (図2)。多くの空きメモリーがあったとしても、ライブ・オブジェクトが散在してメモリーが細かく分断されていると、それぞれの大きさが十分でないために特定の割り振りに利用できない場合があります。
この解決策が圧縮です。理論的には、この圧縮機能はすべてのライブ・オブジェクトをヒープの一端に移動してまとめ、空きスペースの連続ブロックを 1 つ確保します。すべてのライブ・オブジェクトを移動することになる可能性もあり、移動したオブジェクトへのポインターをすべて新しい場所に合わせて更新する必要もあるので、この操作には少なからぬ時間とリソースを要します。そのため、圧縮は通常、必要と判断された場合にのみ行われます。圧縮も並列で行うことができますが、ライブ・オブジェクトの圧縮が効率的でなくなり、1 ブロックの空きスペースの代わりに、複数の小さいブロックが作成されることがあります。
図2. ヒープのフラグメント化
全体的なスループットよりも一時停止の時間が短いことが優先されるアプリケーションには、別のポリシーを使用できます。optavgpause ポリシー (-Xgcpolicy:optavgpause) では、アプリケーションを停止する前にできるだけ多くの GC 作業を行おうとするため、一時停止の時間が短くなります (図3)。ここでも、同じ mark-sweep-compact コレクターが使用されますが、アプリケーションの実行中に、mark フェーズと sweep フェーズの多くをこなすことができます。システムでは、プログラムの割り振り速度に基づいて、次のガーベッジ・コレクションが必要になる時期を予測します。このしきい値に近づくと、コンカレント GC が開始されます。アプリケーション・スレッドでオブジェクトを割り振る際、割り振りが完了する前に少量の GC 作業を行うように求められることがあります。発生する割り振りが多いスレッドほど、この GC で多くの作業を求められます。その間、1 つまたは複数のバックグラウンド GC スレッドで、アイドル・サイクルを使用して別の作業が行われます。すべての並行作業が完了するか、または予定より早く空きメモリーが消費されると、アプリケーションが停止し、コレクションが実行されます。通常、この一時停止は短時間です。ただし、圧縮が必要な場合は除きます。圧縮は、ライブ・オブジェクトの移動と更新が必要なため、並行して行うことはできません。
図3. アプリケーションとコレクターの CPU 使用状況: optavgpause
作成されたオブジェクトの大部分が短期間しか使用されないことが、かなり以前から確認されています。これは、プログラミング手法とアプリケーションの種類の両方に起因しています。多くの一般的な Java イディオムでは、StringBuffer/StringBuilder オブジェクトや Iterator オブジェクトなど、短時間で破棄されるヘルパー・オブジェクトが作成されます。これらのオブジェクトは特定のタスクを行うために割り振られるので、後で必要になることはめったにありません。より大きな規模では、本来はトランザクショナルなアプリケーションでも、まとめて使用された後で破棄されるオブジェクトのグループが作成される傾向があります。データベース照会に対する応答が返された後は、応答、中間状態、および照会自体が不要になります。
これらのことから、世代別ガーベッジ・コレクターが開発されました。概念としては、ヒープを異なる領域に分割し、それらの領域において回収を異なる割合で行うというものです。新しいオブジェクトは、nursery (New スペース) と呼ばれる、このような領域の 1つに割り振られます。この領域のほとんどのオブジェクトはすぐにガーベッジになるため、この領域での回収はメモリーを回復する絶好の機会となります。しばらくの間残っているオブジェクトは、tenure (Old スペース) と呼ばれる別の領域に移動します。これらのオブジェクトはガーベッジになる可能性が低いため、コレクターで調査する回数ははるかに少なくなります。通常のワークロードの場合、調査対象となるメモリーが少なく、調査済みのオブジェクトを再利用する確率が高いため、より迅速で効率的なコレクションが実現します。コレクションが迅速になると、一時停止の時間が短くなるため、アプリケーションの応答性が向上します。
IBM の gencon ポリシー (-Xgcpolicy:gencon) では、前述のコンカレント GC (「-con」) に加えて世代別 GC (「gen-」) が提供されています。tenure スペースは前述のように回収されますが、nursery スペースではコピー・コレクターが使用されます。このアルゴリズムは、nursery 領域を allocate スペースと survivor スペースにさらに細分することによって機能します (図4)。allocate スペースの空きスペースがすべて使用されるまで、新しいオブジェクトは allocate スペースに配置されます。空きスペースがなくなると、アプリケーションが停止し、allocate スペース内のライブ・オブジェクトは survivor スペースにコピーされます。その後、2つのスペースが役割を交代し、survivor スペースが allocate スペースとなって、アプリケーションが再開されます。これらのコピーが何回か行われた後も残っているオブジェクトは tenure 領域に移動します。
図4. gencon の動作
理論的には、nursery スペースの半分 (つまり survivor スペース) は常に未使用であることになります。実際のところ、survivor スペースのために確保されるメモリーの量は、各コレクションの後まで残っているオブジェクトの割合に応じて、処理中に調整されます。新しいオブジェクトの大半が回収される場合 (このような状況はあり得ます)、allocate と survivor の間の境界線は斜めになり、コレクションが必要になる前に割り振ることができるデータの量は増加します。
この種類のコレクターには重要なメリットがあります。つまり、コレクションごとにライブ・オブジェクトを移動することによって、nursery 領域はコレクションのたびに暗黙的に圧縮されます。その結果、最大限の空きスペースのブロックが作成されますが、同時に、密接に関連するオブジェクト (String とその char[] データなど) が、隣接するメモリーの場所に移動する傾向があります。そのため、システム・メモリー・キャッシュ、さらにはアプリケーション自体のパフォーマンス特性が向上します。
nursery ガーベッジ・コレクションのコストは、生存しているデータの量に関係します (図5)。ほとんどのオブジェクトがガーベッジになると考えられるので、通常、nursery コレクションで生じる一時停止は非常に短時間です。オブジェクトの大半はすぐに回収されますが、そうでないものもあります。時間の経過に伴って、長く生存しているオブジェクトで tenure 領域がいっぱいになり、ヒープ全体のガーベッジ・コレクションが必要になります。そこで、コンカレント・コレクターについて前述した手法のほとんどをここでも適用します。nursery で割り振りと回収が行われている間、必要に応じて、tenure 領域の mark 処理を並行して実行します。gencon では、tenure 領域の sweep 処理は並行して行われず、メインの tenure コレクションの一環として行われます。
図5. アプリケーションとコレクターの CPU 使用状況: gencon
領域ベースのコレクター: balanced
WebSphere Application Server V8 には、新しいガーベッジ・コレクション・ポリシーが追加されています。balanced (-Xgcpolicy:balanced) と呼ばれるこのポリシーは、ヒープの中のさまざまな領域を使用するという概念をさらに拡張したものです。ヒープが多数の領域に分割されていて、それらを別々に処理できます。領域ベースのガーベッジ・コレクション全般および balanced ポリシーの詳細については、パート2で説明します。
アプリケーションのヒープ・サイズを調整する最初の手順は、デフォルトのヒープ設定を使用してアプリケーションを実行することです。これによって、設定を変更していない状態のパフォーマンスを測定できます。その時点で、ヒープの空きが一貫して 40%を下回っている場合、または GC による一時停止時間が合計実行時間の 10%を超えている場合は、ヒープ・サイズを大きくすることを検討する必要があります。ヒープの最小サイズおよび最大サイズを変更するには、-Xms<value> および -Xmx<value> をそれぞれ指定します。
ガーベッジ・コレクションの mark フェーズおよび sweep フェーズにおいて、GC による一時停止で費やされる時間は、ヒープ上のライブ・オブジェクトの数に基づきます。変動が少ないワークロードでヒープ・サイズを大きくしても、mark フェーズと sweep フェーズに要する時間はほとんど変わりません。つまり、ヒープ・サイズを大きくすることによって、GC による一時停止の発生間隔が長くなり、アプリケーションを実行できる時間が長くなります。
フラグメント化の問題が原因で GC が compact フェーズを実行している場合、ヒープ・サイズを大きくすると、圧縮による長い停止時間が短縮される可能性があります。compact フェーズでは GC による停止時間が大幅に長くなる傾向があるため、圧縮が定期的に行われる場合は、ヒープ設定を調整することでアプリケーションのパフォーマンスを向上させることができます。
可変サイズのヒープを使用すると、GC では、アプリケーションでヒープに対して必要とする OS リソースのみを使用できます。アプリケーションのヒープ要件が変わると、GC はヒープを拡大または縮小することによって対応します。GC では、ヒープの端からでないとメモリーの連続ブロックを縮小できないので、ヒープを縮小する際に圧縮が必要になる場合があります。実際の縮小フェーズおよび拡大フェーズは非常に短時間なので、GC による一時停止時間が著しく長くなることはありません。通常の操作に必要とされるよりも最大ヒープ・サイズを大きく設定しておくと、ヒープを拡大することによって、アプリケーションで通常よりも多いワークロードを処理できるようになります。
ヒープ要件が一貫しているアプリケーションでは、固定ヒープ・サイズを使用することで、GC による一時停止時間が短縮される可能性があります。
世代別ガーベッジ・コレクションを調整する場合、最も簡単なアプローチは、世代別以外の場合に使用される Java ヒープ領域に加えて、nursery スペースを新たな Java ヒープ領域として扱うことです。つまり、世代別以外の場合の Java ヒープは tenure ヒープとなります。
これは、従来からある手法です。nursery の導入によって tenure ヒープの占有率は低下すると思われますが、世代別以外のポリシーから移行する場合は特に、このアプローチが安全な開始点となります。グローバル (フル) コレクション後の tenure ヒープの占有率を監視できる場合は、前述のようにサイズを調整できます。
-Xmn<size> では、nursery の初期サイズおよび最大サイズを設定して、実質的に -Xmns と -Xmnx の両方を設定します。
-Xmns<size> では、nursery の初期サイズを指定の値に設定します。
-Xmnx<size> では、nursery の最大サイズを指定の値に設定します。
nursery ヒープのサイズは固定値とする必要があります。そのため、これらのオプションのうち、-Xmn のみが必須です。したがって、nursery ヒープのサイズを正しく設定する方法を理解すればよいだけです。
nursery のサイズを正しく設定するには、まず、nursery コレクションで使用されるメカニズムと、その結果として生じる副次的な特徴を考慮する必要があります。
- nursery コレクションは、allocate スペースから survivor スペースにデータをコピーすることによって機能します。データのコピーは、かなりのコストと時間を要する作業です。そのため、nursery コレクションの期間は、コピーするデータの量によって決まります。ただし、コピーするオブジェクトの数や nursery 自体のサイズの影響を受けないということではなく、実際のデータをコピーするコストと比べると、それほど重大ではないということです。その結果、nursery コレクションに要する時間は、コピーするデータの量に比例します。
- 特定のコレクションで「ライブ」であるのは、限られた固定量のデータのみです。アプリケーションの起動が完了し、例えば、データがすべてキャッシュに収まると、nursery ヒープにコピーする必要がある「ライブ」データの量は、その時点で実行している作業の量によって確定します。トランザクションを処理するシステムでは、コピーする必要があるライブ・データの量は 1 組のライブ・トランザクションに相当します。例えば、50 個の WebContainer スレッドでアプリケーション・サーバーを構成しており、50 個の並行トランザクションを実行できる場合、ライブ・データは、その 50 個のトランザクションに応じた量になります。
つまり、nursery コレクションの期間は、nursery自体 のサイズではなく、コレクションの時点で実行されている並行トランザクションの数に応じたデータのサイズによって設定されます。また、nursery自体 のサイズが大きくなるにつれて、nursery コレクションの実行間隔が長くなりますが、コレクションの所要期間が長くなることはありません。実際のところ、nursery自体 が大きくなると、ガーベッジ・コレクションに要する全体的な時間は短くなります。
図6 は、次のことを示しています。nursery 自体のサイズが、1 組のトランザクションに対応するライブ・データよりも小さく、そのために各 nursery コレクションの間隔が 1 つのトランザクションよりも短い場合、データは複数回コピーされる必要があります。
図6. データの平均コピー回数と nursery コレクション間の時間の関係
nursery自体 のサイズが大きくなり、nursery コレクションの実行間隔が長くなると、コピー回数が平均して少なくなるため、ガーベッジ・コレクションのオーバーヘッドが軽減されます。
IBM ガーベッジ・コレクターまたは JVM によって nursery ヒープのサイズに直接の制限はありません。実際には、nursery が数十ギガバイト、さらには数百ギガバイトというサイズに設定される場合もあります。ただし、オペレーティング・システムによって生じる制限はあります。十分な量の利用可能な物理メモリー (RAM) のほか、仮想メモリーやプロセス・アドレス・スペースに関しては、Java プロセスが準拠しなければならない制限事項があります。32 ビット・プロセスに関するプラットフォームごとのオペレーティング・システムの制限を図7に示します。
図7. オペレーティング・システムごとの 32 ビット・アドレス・スペース
64 ビット・プロセスの制限は、これよりもはるかに大きくなります。数百ギガバイトから数十億ギガバイトのアドレス可能メモリーでは、利用可能な物理メモリー (RAM) の制限がはるかに重要になります。
前述のとおり、最も簡単なアプローチは、nursery を追加メモリー・スペースとして扱うことです。しかし実際には、nursery ヒープも tenure ヒープもメモリーの 1 つの連続セグメントとして割り振られ、そのサイズは -Xmx 設定によって制御されます。-Xmx 設定のみを使用した場合、-Xmx 値の 25% が nursery の最大サイズとして使用され、nursery のサイズはその 25% の範囲で増減できます。この場合、Java ヒープのレイアウトは図8のようになります。
図8. デフォルトのヒープ・レイアウト
しかし、ガーベッジ・コレクションに要する時間を最小限に抑え、占有率に従って tenure ヒープのサイズを調整できるようにして弾力性を確保するには、nursery のサイズを大きい値で固定する必要があります。したがって、Java ヒープの推奨されるレイアウトは図9のようになります。
図9. 推奨されるヒープ・レイアウト
このレイアウトを実現するには、nursery スペースと tenure スペース両方の最小と最大のヒープ・サイズの値を設定する際に、nursery の最小サイズと最大サイズの設定値を等しくし、tenure スペースの最小サイズと最大サイズは異なる値に設定する必要があります。
例えば、nursery ヒープのサイズを 256MB にし、tenure ヒープを 756MB から 1024MB にする場合、値は次のようになります。
-Xmns256M
-Xmnx256M
-Xmos756M
-Xmox1024M
WebSphere Application Server V8 ではデフォルトの GC ポリシーが optthruput から gencon に変更されているため、既に選択しているチューニング・パラメーターを調整しなければならない場合があります。重要な点は、nursery を補うようにヒープ・サイズを変更することです。1G のヒープ (-Xmx1G) を指定した optthruput の下で、それまで正常に動作していたプログラムが、768M の tenure スペースと 256M の nursery スペースを設定しただけで適切に動作しなくなることがあります。新しいヒープ・パラメーターを選択するには、前述の手法が役立ちます。
これほど明らかではありませんが、gencon が異なる動作を示す可能性がある状況は他にも考えられます。
クラスは通常、生存期間の長いオブジェクトであるため、tenure スペースに直接割り振られます。そのため、クラスのアンロードは tenure コレクションの一環としてのみ実行できます。生存期間の短いクラス・ローダーにアプリケーションが大きく依存し、割り振られた他のオブジェクトには nursery コレクションで対処できる場合、tenure コレクションがあまり頻繁に実行されない可能性があります。これは、クラスおよびクラス・ローダーの数が増え続けることを意味します。その結果、クラスのアンロード作業が膨大に実行されるようになるので、tenure コレクションの際にネイティブ・メモリーに対する圧力が大きくなり、tenure コレクションの時間が非常に長くなる可能性があります。
この点が問題となる場合、2 つの解決策があります。1 つ目の解決策は、クラス・ローダーが大量にある場合、追加の tenure コレクションの実行頻度を高くすることです。コマンド・ライン・オプション -Xgc:classUnloadingKickoffThreshold=<number> を使用すると、新しいクラス・ローダーが <number> 個作成されるごとに、コンカレント tenure コレクションを開始するように指定できます。例えば、-Xgc:classUnloadingKickoffThreshold=100 を指定した場合、前回の tenure コレクション以降、新しいクラス・ローダーが 100 個作成されたことが nursery コレクションでわかると、コンカレント tenure コレクションが開始されます。2 つ目の解決策は、他の GC ポリシーのいずれかに変更することです。
参照オブジェクト (java.lang.ref.Reference のサブクラスなど) や、finalize() メソッドを持つオブジェクトでも同様の問題が生じることがあります。これらのオブジェクトのいずれかが、到達不能にならないままで tenure スペースに移動できるくらい長時間存続している場合は、tenure コレクションを実行しても、そのオブジェクトがデッド状態となっていることを「認識」するまでに時間がかかることがあります。これらのオブジェクトが大量のネイティブ・リソースを使用している場合、またはネイティブ・リソースが不足している場合、これが問題となる可能性があります。このようなオブジェクトを「氷山」オブジェクトと呼んでいます。専有する Java ヒープは少量ですが、ガーベッジ・コレクターに認識されない大量のネイティブ・リソースが表面下に潜んでいるためです。実際の氷山と同様に、このようなオブジェクトに起因する問題を可能な限り避けることをお勧めします。他のどの GC ポリシーでも、ファイナライズ可能なオブジェクトが到達不能として検出され、そのファイナライザーが適切な時点で実行されるという保証はありません。リソースの不足を管理する場合、可能な限り手動で解放することがいつでも最善の方法です。
ほとんどのワークロードでは、デフォルト・ポリシーで十分なパフォーマンスが得られますが、アプリケーションによっては最適な選択肢ではない場合もあります。
「バッチ・ジョブ」のように動作するアプリケーションでは初期状態が設定され、操作するデータがロードされます。このようなオブジェクトのほとんどはジョブの期間中生存し、ジョブの実行中に新しく作成されるオブジェクトはごく少数です。作業が完了するまでガーベッジ・コレクションはほとんど実行されないと考えられるので、このようなワークロードは optthruput モデルに適しています。よく似たケースで、非常に短時間で完了するジョブや、ごく少数のオブジェクトを割り振るジョブは、ガーベッジ・コレクションで適切なサイズのヒープを指定しなくても実行できる可能性があります。このような場合、オーバーヘッドが最小限に抑えられる optthruput コレクターが適しています。
対照的に、トランザクショナルなアプリケーションでは、オブジェクトのグループの作成と破棄が繰り返されます。ここでいう「トランザクション」という用語は、データベースの更新や e-コマースの購買など、まさに文字通りである場合もあれば、独立した作業単位というはるかに広い意味で使用される場合もあります。例を挙げると、Web ページの提供を 1 つのトランザクションと見なすことができます。クライアントが URL を指定すると、サーバーがページの内容を計算して、クライアントに送信します。クライアントがページを受け取ると、サーバーは、計算したデータを破棄できます。
定義をもう少し拡大するために、標準的なユーザー・インターフェースを取り上げます。ユーザーが「保存」ボタンをクリックすると、ファイル・ダイアログが開き、ファイル・システムをナビゲートして文書の場所を選択できます。ユーザーがダイアログを終了すると、その中間状態はすべて不要になります。バッチ・ジョブでさえも、表面下では実際にはトランザクショナルなものがあります。大きい画像ファイルの集合のサムネールを作成する作業は、1 つの大きなバッチ・ジョブのように思えますが、内部では個々に画像を処理しており、それぞれがある種のトランザクションを形成しています。このようなワークロードには、gencon モデルが効果的です。
optavgpause モデルはこれらの中間的な存在です。プログラムの実行に伴ってゆっくりと変化する、生存期間の長いデータが多数あるアプリケーションは、このモデルに適しています。これは、ワークロードの一般的なパターンではありません。通常、生存期間が長いデータはほとんど変更されることがないか、頻繁に変更されるかのどちらかです。つまり、中間オブジェクトを多数生成せずにゆっくり進展するデータ・セットを持つシステムには、このポリシーが効果的です。前述のいずれかの問題が原因となって gencon で効率的に実行できないプログラムにも、optavgpause の並行性が有効である可能性があります。
この記事では、WebSphere Application Server V8 の Java 仮想マシンで使用可能なガーベッジ・コレクション方針について簡単に説明しました。ほとんどの場合はデフォルト設定で適切に動作しますが、最適なパフォーマンスを確保するには、多少の調整が必要になることがあります。ワークロードの種類に適した GC ポリシーを使用し、適切なヒープ・パラメーターを選択することによって、アプリケーションに対するガーベッジ・コレクションの影響を軽減できます。
このシリーズのパート 2 では、balanced と呼ばれる、領域ベースの新しいガーベッジ・コレクション方針を紹介します。balanced は、大規模な 64 ビット・マルチコア・システムに導入した場合にスケーラビリティーが向上するように設計されています。この記事では、この新たなテクノロジーの推進要因となった背景と、そのテクノロジーによって実現するパフォーマンスの向上に加え、この新しいオプションの調整に関するヒントについて説明します。
学ぶために
- ウィキペディア: ガーベッジ・コレクションの定義
- Java technology, IBM style: Garbage collection policies, Part 1
- Java technology, IBM style: Garbage collection policies, Part 2
- WebSphere Application Server V8 の製品情報
-
IBM developerWorks WebSphere
製品や技術を入手するために
-
IBM SDK for Java のダウンロード
-
IBM Monitoring and Diagnostic Tools for Java - Health Center
-
IBM Monitoring and Diagnostic Tools for Java - Garbage Collection and Memory Vizualiser
- WebSphere Application Server V8 評価版のダウンロード
Chris Bailey は、英国の Hursley Park Development Lab を拠点とする IBM JTC (Java Technology Center) チームの一員です。IBM Java サービスおよびサポート組織でテクニカル・アーキテクトを務める彼は、IBM SDK のユーザーをアプリケーション・デプロイメントの成功に導くことを任務としています。また、新しい要件の収集と評価、新しいデバッグ機能とツールの実現、資料の改善、そしてIBM SDK for Java の全体的な品質改善にも携わっています。
Charlie Gracie は、カナダの Ottawa Lab を拠点とする IBM Java Technology Center チームの一員です。University of New Brunswick でコンピューター・サイエンスの学士号を取得し、2004 年から J9 Virtual Machine チームに所属しています。現在は、J9 Garbage Collection チームのチーム・リーダーを務め、IBM JVM のすべてのガーベッジ・コレクション・テクノロジーの提供に携わっています。仕事以外では、インドアとビーチ両方のバレーボールのほか、さまざまなアウトドア・アクティビティーを楽しんでいます。
