大規模化・安定稼働・開発効率化…Webシステム開発・運用を乗り切るテクニック

第2回チューニング② Java VMによるメモリ管理

はじめに

現在のWebシステム開発・運用で踏まえるべき新しい技術的な取り組みについて、日立のアプリケーションサーバ(APサーバ)であるCosminexus(コズミネクサス)の製品群を題材として取り上げながら解説する本連載、前回は流量制御およびDB(I/O)の最適化について触れました。

第2回は、Java仮想マシン(Java VM)によるメモリ管理が主題です。Webシステムにおいては、JavaVMのメモリ管理機能であるガベージコレクション(GC)が性能劣化を引き起こすことが知られています。ここでは日立が開発したJava VMにおけるメモリ管理技術や、日本語処理の高速化を中心に解説します。

GCの基本的なしくみと問題点

GCとは、作業領域のうち使用済みの領域を破棄して空き領域を整列させるしくみのことです。Java VMでは、自身が管理するメモリ領域(ヒープ領域)において使用されなくなった領域の破棄により空き領域を確保するため、GCを実行します。

Java VMのメモリ領域

Java VMのヒープ領域は、Javaアプリケーションが使用する「Javaヒープ⁠⁠、クラスなどのメタ情報を格納する「Permヒープ⁠⁠、JavaVM自身やCプログラムが使用する「Cヒープ⁠⁠、スレッドごとに保持するスタック領域である「スレッドスタック」に分けられます。ほかの領域と比べてJavaヒープではトランザクション処理中のメモリ使用領域の変動が多く、GCも頻繁に発生します。

Javaヒープはさらに、⁠New領域」「Old領域」に大別できます。New領域は、できたてのインスタンスを配置する「Eden領域」と使用中のインスタンスを配置する「Survivor領域」で構成されます。Old領域には「業務プログラム使用領域」⁠J2EEサーバの使用領域」といった寿命の長いインスタンスを配置します図1⁠。

図1 Javaヒープ
図1 Javaヒープ

速いCopy GCと遅いFull GC

JavaヒープがNew領域とOld領域の2つの世代でメモリ管理するのに伴って、Java VMにおけるGCには、⁠Copy GC」「Full GC」の2種類があります。

Copy GCはNew領域を対象に使用済みのインスタンスをすべて削除する処理で、Eden領域がいっぱいになると発生します。処理の所要時間は0.01~0.7秒程度(メモリ容量が500Mバイト~1Gバイト程度の場合)とごく短く、システムへの影響は軽微です。

一方のFull GCはJava ヒープ全体を対象とし、Old 領域の業務プログラム使用領域がいっぱいになると発生します。所要時間は1 ~数十秒と長いことに加えてGC の実行中は業務処理が停止するため、頻発するとシステムの性能劣化を招いてしまいます。

Java VM ベースのシステムでパフォーマンス低下を回避するには、Full GC の発生をいかに抑止するかがカギと言えます。

アプリの変更なしにパフォーマンスを改善可能

既存のJava VM システムではFull GC の抑制が難しく、⁠Java では大容量メモリの扱いが難しい」というのが通説です。Java VM 自体に抑制するしくみがないため個々のシステムごとの対処を余儀なくされ、普遍的で有効な対策を見出すことは困難でした。

そのためCosminexus では、Full GCによるシステムスローダウンの課題を解決するために、これまでの運用で回避するという発想を転換し、Java VMに明示管理ヒープ領域(Eヒープ(Explicit Heap)領域)を新設して、これをGC対象外とする「Eヒープ方式」を採用することにより、Full GCレスを実現しました。

この方式では、セッションオブジェクトなど、あらかじめ長寿命であることがわかっているオブジェクトをEヒープ領域へ自動的に割り当て、解放を行うことでOld領域に配置されるオブジェクト量を削減し、Old領域の業務プログラム使用領域がいっぱいになると発生するFull GCを抑止します。いったんNew領域に割り当てられたセッションオブジェクトは、New領域からEヒープ領域へCopy GCで移動するためCopy GCの所要時間は増加するものの、この所要時間は前述のとおりごく短いため、システム全体のパフォーマンスにはほとんど影響しません図2⁠。

図2 Full GCレスでオンライン業務の一時停止を回避
図2 Full GCレスでオンライン業務の一時停止を回避

日立が試算した例では、従来方式で0.253 秒だった平均Copy GC 時間がE ヒープ方式では0.328 秒に30% 増加し、処理件数は54 万8,819 件から54 万5,486件へと0.61% の劣化でした。つまり、処理性能への影響は1% 未満に留まっています表1⁠。

表1 Copy GC時間増によるスループットの試算例
測定単位時間10:00~15:45(20,70秒)
平均Copy GC時間0.253秒/回 →0.328秒/回(30%増)
処理件数1548,819件 → 545,486件(061%劣化)
           ↓
      処理性能への影響1%未満

開発者の立場からすると、メモリ管理方式の変更によってプログラムを変更しなくてはならないのではとの懸念がありますが、Cosminexus のFull GC レス機能は、Java VM とAP サーバが連携してシームレスなE ヒープ領域化を実現しているため、プログラム側ではまったく意識する必要がありません。既存アプリの変更なしに、新しいメモリ管理方式によるパフォーマンス向上の恩恵を受けられるのです。

誤った領域削除時にも堅牢性を確保

このようにJava ヒープ内に滞留するセッションオブジェクトはE ヒープ領域へ自動的に移動されますが、DB データのキャッシュなどセッションオブジェクト以外の長寿命なオブジェクトを手動でE ヒープ領域へ配置し、不要になったら削除することもできます。

CosminexusのEヒープ方式は、誤って領域を削除してしまった場合においてもシステムの堅牢性を確保しています。たとえば、まだ使用中のデータが存在するEヒープ領域を単純に削除してしまうと、削除済み領域の参照が発生してシステムダウンという深刻な問題が生じてしまいます。そこで、Cosminexusでは領域の削除時に使用中のオブジェクトがあるとJavaヒープへ自動的に移動するため、誤って領域を削除しても問題なく動作を継続します。この技術により、あるアプリの誤ったメモリ領域削除がシステム全体に悪影響を及ぼしてしまう、ということがなくなるのです。

メモリリークの原因特定

Java VMのメモリ管理に起因するトラブルとして、メモリリークも見過ごせない問題です。

メモリリークとは、開発者が予想しない個所でオブジェクトの参照が残り、解放されないJavaオブジェクトが継続的に増加してしまう現象です。メモリリークが発生するとメモリの使用量が増大していき、一定の値に達するとメモリ不足によるシステムダウンなどを引き起こします。当然ながら解消すべき問題なのですが、原因の特定には困難が伴うため、明らかにメモリリークが発生しているのに原因を把握できない事態がしばしば見受けられます。

ヒーププロファイル機能で高精度の解析を実現

その解としてCosminexusが打ち出したのが、メモリリークの原因を特定するための「ヒーププロファイル機能」です。その基本的なしくみは、まずオブジェクト単位のメモリ消費量を把握したうえで、多くのメモリを消費しているオブジェクトを確保しているモジュールを把握するというものです。これにより運用管理者が開発者に問題点を指摘でき、仕様なのかバグなのかを開発者が判断する一助となります図3⁠。

図3 Cosminexusだけでメモリリークの原因を追求
図3 Cosminexusだけでメモリリークの原因を追求

ヒーププロファイル機能では、実稼働環境でメモリリークが発生した時点でサーバを再起動せず情報の取得が可能であり、テスト環境では再現が困難なメモリリークの不具合も早期に解決できます。またJavaVM自身がGCと同じロジックでクラス依存関係の情報を取得するため、高精度の解析が可能という利点があります。通常実行時のオーバヘッドはゼロであり、情報取得時でもFull GCの1回分程度とオーバヘッドが低いのも特長です。

日本特有の処理への配慮

日本国内のユーザを対象とするシステムでは、Javaシステムにおけるプログラミングで考慮すべき一般的なポイントに加えて、たとえば日本語処理など日本独特の事情にも配慮する必要があります。

もちろんアプリケーション側での工夫も有効ですが、Java VMなどプラットフォーム側で対策が取られていれば、開発における労苦はかなり軽減されます。

日本語環境での文字列処理高速化

Javaシステムで日本語を扱う場合、OSやDBが使用する文字コードはシフトJIS(SJIS)やEUCが大半ですが、Java VM内部ではUnicodeを使用します。SJISやEUCとUnicodeとのコード変換処理が頻繁に発生するため、これを高速化することによりシステム全体のパフォーマンス改善が図れます。コード変換は全角文字の処理に加え、意外と見落とされがちですが半角文字の処理でも発生する、いわばJavaシステムの文字列処理における"足回り"的な重要部分と言えます。

CosminexusのJava VMではJavaクラスライブラリのStringクラス内部処理を改善することにより、5~6倍以上のコード変換処理の高速化を実現しています。

文字列操作や日付取得ライブラリの最適化

Javaシステムの処理時間を処理カテゴリごとに分類すると、ほとんどのケースで文字列操作が最上位にランク付けられるため、文字列操作処理も高速化することがシステム全体のパフォーマンス向上につながります。CosminexusのJava VMでは、文字列処理を担うクラスライブラリのチューンナップにより、文字列の比較や結合などのさまざまな処理を数倍~数十倍に高速化しています。

また、帳票出力などを主業務とするJavaシステムでは、日付の問い合わせを頻繁に実行しています。CosminexusのJava VMでは、日付の取得を行うクラスライブラリをチューンナップして最大で10倍の高速化を可能にしました。

独自の取り組みによる強化

CosminexusのJava VMはこのほかにも、スレッド固有領域へのアクセスの高速化、Just In Timeコンパイラ(JITコンパイラ)の最適化性能の向上、スレッド間の同期/排他を行うロック処理のプラットフォームごとの最適化による最小コストでのロック処理の実装などにより、高性能・高信頼化を図っています。

これらの取り組みにより、APサーバとしてのCosminexusの信頼性向上に結びついています。また、Java VMにおける各種の機能強化やチューニングなどは、お互いの特長を活かせるようにCosminexusの機能と連携しており、両者は密接不可分な関係にあるのです図4⁠。

図4 高品質Javaシステムへの取り組み
図4 高品質Javaシステムへの取り組み

次回予告

次回は、障害情報の収集障害解析と復旧などの側面について、APサーバで対応可能な対策を中心に解説します。

おすすめ記事

記事・ニュース一覧