メモリー管理に失敗したJavaアプリの実例

2011年3月11日(金)
東 浩二(A-pZ)(監修:山田祥寛)

用途に合わせた実装を選択

高性能なサーバーであっても、リソースはもちろん有限です。アプリケーションが取り扱うデータ量は、概算であっても見積もるのは当然ですが、負荷がかかりそうな機能にスポットを当てる必要もあります。その処理が扱うであろうデータ件数を見積もり、他機能に影響しない範囲でメモリーに展開しても良いかどうかを計測するのです。利用状況に応じて、コードの修正をするべきか、利用制限を設けて回避するかなども、選択肢として考えられるでしょう。

では、実際に先ほどの問題を解決した方法を挙げていきます。

  • メモリー使用量を減らすため、iBatisの検索処理を、1行ずつ処理するRowHandlerに変更して、順次CSVへ出力するようにした

改修した処理の概要は、以下の図のようになります。

図2: 大量にメモリーを消費するコードへの対処例

図2: 大量にメモリーを消費するコードへの対処例

コードは、以下のようになります。

CSVHandlerは、iBatisが提供するクラスを継承したもので、CSVファイルへ検索結果を1行ずつ出力する処理になります。

以上のようにコードを改修することで、iBatisから取得するオブジェクトは1件ずつになり、逐次CSVへ出力するようになります。これにより、検索でメモリを大量消費することはなくなりました。しかし、これだけですべてが解決したわけではなく、次の問題が浮き彫りとなります。

レスポンス遅延への対処

一度解決したメモリー不足への対応ですが、対応の方法が不十分であると、問題が解決しないばかりか、さらに重症化します。

  • 処理終了まで時間がかかる機能だったため、サーバーの負荷状況によっては待機時間が長くなり、ブラウザがタイムアウトして、結果を表示しなくなってしまう
  • 特にエラー通知がないので、ユーザーが何度か実行してしまう。これにより、負荷が高い処理が複数実行されてしまい、他機能のレスポンスも大幅に低下する

この場合、利用者側は、システムが停止したとは思わずに、何度か同じ処理を行います。ブラウザがタイムアウトしていてもサーバー側では処理が続行しているので、制限をかけなければ立て続けに高負荷の処理が走ることになります。そうなると、アプリケーション全体が遅くなっているけれどもシステム・エラーは報告されない状況となります。遅延だけでなく、場合によってはデータベースのリソースを取得できないようになり、いずれシステムが停止してしまうでしょう。

こうなってしまうと、ユーザー側に「利用時の制限」として伝えて障害を回避するか、さらに改修を行ってブラウザ上でタイムアウトしないよう監視用の画面を定期的に出力するようにしなければなりません。さらに、アプリケーション側でも、複数の処理が同時に走らないよう排他処理を追加実装することになります。

このように対応が後手後手になってしまうのは避けなければなりません。これを避けるためにも、事前にアプリケーションの負荷やデータ量を見積もる作業を行い、問題が起こりそうな機能の検証を行う必要があります。設計の見直しとヒープ・サイズの見直し、要件と場合によってはハードウエアの増設も検討することができたでしょう。

このケースのように、アプリケーションのサイジングをしなければならない場面が、必ずあります。サイジングは、初期構築時だけでなく、拡張時にも必要です。では、Javaアプリケーションのサイジングは、どのような場面で必要になるのでしょうか。その切り口を追ってみましょう。

著者
東 浩二(A-pZ)(監修:山田祥寛)
WINGSプロジェクト

有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表:山田祥寛)。おもな活動は、Web開発分野の書籍/雑誌/Web記事の執筆。ほかに海外記事の翻訳、講演なども幅広く手がける。2011年3月時点での登録メンバは36名で、現在もプロジェクトメンバーを募集中。執筆に興味のある方は、どしどしご応募頂きたい。著書多数。
http://www.wings.msn.to/

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています