- PR -

RMIでOutOfMemoryが発生する

投稿者投稿内容
かい
常連さん
会議室デビュー日: 2003/05/27
投稿数: 34
投稿日時: 2004-09-02 13:52
かいです。

RMIを使用して、通信を行っています。
大量なデータ(1データオブジェクト 4Kバイト×3万件)を送ろうとすると、
OutOfMemoryが発生します。
(データオブジェクトのサイズは、シリアライズしたサイズです)

5000件までは、問題なく動作します。

以前に、このような問題を対処した方がいましたら、
対処方法を教えてください。
よろしくお願いします。

JDK:1.4.2-b28
OS:Windows2000

以下、エラーメッセージです。
真ん中の当たりで、OutOfMemoryが出力されています。
=================================================
<record>
<date>2004-09-02T13:26:05</date>
<millis>1094099165828</millis>
<sequence>140</sequence>
<logger>sun.rmi.loader</logger>
<level>FINER</level>
<class>sun.rmi.server.LoaderHandler</class>
<method>loadClass</method>
<thread>13</thread>
<message>RMI TCP Connection(2)-1XX.XX.21.137: class "abc.domainCommonTier.framework.AbstractCondBean" found via thread context class loader (no security manager: codebase disabled), defined by sun.misc.Launcher$AppClassLoader@67ac19</message>
</record>
<record>
<date>2004-09-02T13:26:33</date>
<millis>1094099193500</millis>
<sequence>141</sequence>
<logger>sun.rmi.server.call</logger>
<level>FINE</level>
<class>sun.rmi.server.UnicastServerRef</class>
<method>logCallException</method>
<thread>13</thread>
<message>RMI TCP Connection(2)-1XX.XX.21.137: [1XX.XX.21.137] exception: </message>
<exception>
<message>java.lang.OutOfMemoryError</message> ←ここ
</exception>
</record>
<record>
<date>2004-09-02T13:26:33</date>
<millis>1094099193765</millis>
<sequence>142</sequence>
<logger>sun.rmi.transport.misc</logger>
<level>FINER</level>
<class>sun.rmi.transport.StreamRemoteCall</class>
<method>getOutputStream</method>
<thread>13</thread>
<message>RMI TCP Connection(2)-1XX.XX.21.137: getting output stream</message>
</record>
山本 裕介
ぬし
会議室デビュー日: 2003/05/22
投稿数: 2415
お住まい・勤務地: 恵比寿
投稿日時: 2004-09-02 13:56
大きなサイズのオブジェクトを扱うと OutOfMemoryError が発生する、というのは至極当然の話です。
最大ヒープサイズを増やして検証してみてはいかがでしょうか。
かい
常連さん
会議室デビュー日: 2003/05/27
投稿数: 34
投稿日時: 2004-09-02 14:07
返信ありがとうございます。

JAVAのメモリ設定を256mから512m
(Xms512m、Xmx512m)
に変更しましたが、ダメでした。

インギさんの指摘にあるように、大きなデータを扱うには、
このサイズを大きくしていくしかないのでしょうか?
(逆にいうと、データを分割して処理するような設計にしなければ
 いけないということでしょうか?)

よろしくお願いします。
山本 裕介
ぬし
会議室デビュー日: 2003/05/22
投稿数: 2415
お住まい・勤務地: 恵比寿
投稿日時: 2004-09-02 15:17
一般的に大きなサイズのオブジェクトをオンメモリで保持するには大きなサイズのヒープが必要になります。
ヒープサイズを拡張することでエラーが発生しなくなるのであれば単純にヒープサイズが不足していることになります。とくにマルチスレッドで動作するアプリケーションでは処理速度などとのトレードオフになりますが一度に利用するメモリはほどほどに押さえるべきでしょう。
ご認識の通り必要に応じてデータを分割して扱うなどの工夫も必要です。
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2004-09-02 15:36
unibon です。こんにちわ。

引用:

かいさんの書き込み (2004-09-02 14:07) より:
JAVAのメモリ設定を256mから512m
(Xms512m、Xmx512m)
に変更しましたが、ダメでした。


疑ってすみませんが、この設定は効いているのでしょうか。配列を new するような簡単なプログラムで効いているかを確認されてはどうでしょうか。
また、もっと大きくはできないのでしょうか。おおよそ物理メモリの大きさまでは指定できます。

引用:

かいさんの書き込み (2004-09-02 13:52) より:
RMIを使用して、通信を行っています。
大量なデータ(1データオブジェクト 4Kバイト×3万件)を送ろうとすると、
OutOfMemoryが発生します。
(データオブジェクトのサイズは、シリアライズしたサイズです)

5000件までは、問題なく動作します。


RMI が絡んでいるのかを切り分けるために、ファイルに書いてみてそれを読み込んでみてはどうでしょうか。これでもメモリ不足になるのでしょうか。また、この場合のファイルサイズはいくつになるかが気になります。
「4Kバイト×3万件」 = 120Mバイトですよね。そんなに大きくはないと思うのですが。シリアライズやデシリアライズでゴミデータを持っていたり、あるいはもし シリアライズやデシリアライズ用のメソッド(writeObject/readObject/writeExternal/readExternal)を持っていたとしたらその実装がマズいとか?
かい
常連さん
会議室デビュー日: 2003/05/27
投稿数: 34
投稿日時: 2004-09-02 18:43
かいです。
インギさん、unibonさん、返信ありがとうございます。

引用:

疑ってすみませんが、この設定は効いているのでしょうか。配列を new するような簡単なプログラムで効いているかを確認されてはどうでしょうか。
また、もっと大きくはできないのでしょうか。おおよそ物理メモリの大きさまでは指定できます。



配列のサイズを大きくして、JVMが起動できるかチェックしてみたところ、
オプションで指定した値は有効になっていました。

引用:

RMI が絡んでいるのかを切り分けるために、ファイルに書いてみてそれを読み込んでみてはどうでしょうか。これでもメモリ不足になるのでしょうか。また、この場合のファイルサイズはいくつになるかが気になります。



試してみます。
ただ、RMIのアプリケーション(クラス)の他にも、DBをアクセスするクラスや
データを保持するstaticクラスがいますので、このファイルサイズだけでは
判断できないかもしれません。
やはり、メモリ不足によるものかもしれないと感じてきました。

RMIに、伝送するデータのサイズによる制限等があるのかと、疑っていました
が、単純なメモリ不足に陥っているのかもしれません。


引用:

「4Kバイト×3万件」 = 120Mバイトですよね。そんなに大きくはないと思うのですが。シリアライズやデシリアライズでゴミデータを持っていたり、あるいはもし シリアライズやデシリアライズ用のメソッド(writeObject/readObject/writeExternal/readExternal)を持っていたとしたらその実装がマズいとか?


シリアライズ用のメソッドは用意して(implementsのみ)いません。
丁寧に、指摘して頂き、ありがとうございます。

ファイルサイズが分かったら、報告します。
かい
常連さん
会議室デビュー日: 2003/05/27
投稿数: 34
投稿日時: 2004-09-02 22:14
かいです。

ファイルサイズは、8Mサイズでした。

リモートメソッドは、先に記載したデータオブジェクト(Beanとする)を
List(ArrayList)に3万件詰めて、返却するようになっています。
上記の8Mは、このListをシリアライズした時のサイズです。

前回、報告した4Kバイトは、Beanをシリアライズした時のサイズでした。
Beanを、Listにつめてシリアライズすると、元のサイズより小さくなるの
ですね。初めて、知りました。(最適化のようなことは、JAVAはしている
のでしょうか?)
unibonさんと同様に、120Mバイトくらいになると思っていたのですが、
あまりの小ささにビックリしました。

ファイルへのI/Oによる結果は、成功しました。シリアライズした後、
でシリアライズして動作しました。
しかし、その先のRMIのところで、OutOfMemoryを検出しています。
(前回のエラーメッセージと同じです)

他に、試した方がよいことがあれば、引き続き、教えてください。
よろしくお願いします。

山本 裕介
ぬし
会議室デビュー日: 2003/05/22
投稿数: 2415
お住まい・勤務地: 恵比寿
投稿日時: 2004-09-02 23:27
いろいろなデータの量で、それぞれどれくらいのメモリを使うのか確認してみてはいかがでしょうか。
5000件ではうまくいくとのことですから、1000件、2000件、3000件、5000件と試したときのヒープ使用量を調べればヒープサイズに対して何件まで対応できるかが計算できるはずです。
どれくらいヒープ領域を使っているかどうかは -verbose:gc でログがとれます。
jtune や侍でグラフ化することも出来ます。
・jtune
http://h50146.www5.hp.com/products/software/development/java/tool/jtune/
・侍
http://yusuke.homeip.net/samurai/

ただ、そもそも RMI ってのは Remote Method Invocation という名前のとおりメソッド呼び出しを目的としたプロトコルで大容量のオブジェクト、ファイルを転送する目的として実装されているわけではありませんのでもしかすると効率が悪いかもしれません。

また、オブジェクトをうけとれたとしてもそのオブジェクトを操作している間にやはりヒープが足りなく鳴ってしまう可能性もあります。やはり件数にかかわらず適切に処理できるよう一定件数で区切りながら処理していくのが良いでしょう。

スキルアップ/キャリアアップ(JOB@IT)