質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.12%

JavaのOutOfMemoryError

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,036

dttornado

score 14

前提・実現したいこと

ルーム機能のないチャットアプリにおいてクライアントアプリからソケット通信を用いて送られてきたメッセージを処理するサーバアプリ

発生している問題・エラーメッセージ

クライアントアプリごとにメッセージを貯めるスレッドのオブジェクトを生成するが、スレッドのrunメソッドのwhileループでメッセージを取得しようとするところでOutOfMemoryError:Java heap spaceがでてしまう、また、オブジェクトのコレクションにためたメッセージを他のスレッドから処理できなくなってしまう
原因と対策を教えてください
よろしくお願いします
追記:スタックトレース
Exception in thread "Thread-4" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at GetMsgThread.run(NChat.java:176)

追記:原因は不明のままですが、対処法については判明しました。SrcThreadのwhileループの1つ下のブロック、つまりforループの前にSystem.out.println()などのなんらかのメソッドを呼び出すと正常に作動することが分かりました。
しかし原因については一切の見当が付きません

該当のソースコード

Java

import 色々
class NChatC{
   staticな変数色々定義
   mainメソッドでコンストラクタ呼び出し
   コンストラクタでフレーム作成、AcceptThread及びSrvThreadをstart()
}
class AcceptThread extends Thread{
   run(){
     ServerSocketをwhileループ内で作成しaccept()され
   されたらaddMember()に引数でわたす
   }
   addMember(Socket sock){
      クライアント毎にsocket,inputstream,outputstrea
      m,ユーザー名、GetMsgThread(inpustreamで取得した文字列をためるスレッド)のオブジェクトのコレ    クションを新たなメンバー用に追加し、新たなメン    バーに現在の参加メンバーを通知し、現在のメンバ    ーに新たな参加者を通知する
   }
}
//問題が発生するスレッド
//クライアントごとにメッセージをためる
class GetMsgThread extends Thread{
    ArrayList<String> newMsg=new ArrayList<String>();
    Inp in;
    boolean flag=true;
    public GetMsgThread(Inp inp){
        in=inp;
    }
    public void run(){
        while(flag){
        newMsg.add(in.inp());
        }
    }
    public String getMsg(){
        String msg=newMsg.get(0);
        newMsg.remove(0);
        return msg;
    }
}
//GetMsgThreadで入手した文字列を処理するスレッド
//socketVはソケットのコレクション
//gmtVはGetMsgThreadのオブジェクト
SrvThread{
   run(){
      while(true){
         for(int i=0;i<NChat.socketV.size();i++){
            String str="";
            if(NChat.gmtV.get(i).newMsg.size()!=0){
                str=NChat.gmtV.get(i).getMsg}
         }
         strの処理
      }
   }
}
class Out{
  OutputStreamをバッファとしてためて出力
}
class Inp{
  InputStreamをバッファにためて
  inp()メソッドで返す
}
//Inpクラスの内容追記
class Inp{
    String str=null;
    BufferedReader br;
    Inp(InputStream ist){
        try{
            InputStreamReader isp = new InputStreamReader(ist);
            br = new BufferedReader(isp);
        }catch(Exception e){
            System.err.print(e);
        }
    }
    String inp(){
        try{
            str = br.readLine();
        }catch(Exception e){
            System.err.print(e);
        }
        return str;
    }
    void inpcls(){
        try{
            br.close();
        }catch(Exception e){
            System.err.println(e);
        }
    }
}

試したこと

オブジェクトやスレッドが解放されてないのかと思い、通信終了時に明示的に終了するようにした(そもそも通信終了処理ができない時点でエラーが発生しているので効果はなかった)

補足情報(言語/FW/ツール等のバージョンなど)

開発当初はこのようなエラーも出ず、最近になってからでるようになった

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • eripong

    2016/07/10 17:48

    in.inp()の実装内容をできるだけ提示してください。OutOfMemoryErrorのスタックトレースも載せてもらえませんか?

    キャンセル

  • dttornado

    2016/07/10 18:10

    実装内容、スタックトレース追記させていただきました。

    キャンセル

  • dttornado

    2016/07/11 11:56

    解決しました。コメントありがとうございました。

    キャンセル

回答 2

check解決した方法

0

なんとなく原因が分かりました。
私はメッセージを処理するスレッドをwhileループで回し続けたまま休ませることをしていなかったのですが(タイムラグが発生すると思い)、そのままだとループに次ぐループでうまく処理をこなせなくなってしまうようです。特にループは全てメッセージについて扱うものでしたからメッセージ自体が自身の処理を行うことができなくなってしまいなんらかの形で処理が滞って本来起こるはずのないOutOfMemoryエラーがでてしまったのかな、と思います。
対処法としては、スレッドを休ませることです。基本ですが大事ですね。これからはもうちょっとJVMに優しくなりたいです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

while(flag){
    newMsg.add(in.inp());
}

これいつflagがfalseになるんでしょうね。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/07/10 17:27

    返信ありがとうございます
    説明不足で申し訳ございません。flagは、クライアントから切断用のメッセージが来るか切断されたらfalseになるようにstrの処理の部分で設定してあります。ただし、そのメッセージが、処理する部分まで到達していません。クライアントからのメッセージはGetMsgThreadまで到達していることは確認済みですがその後行方がわからなくなり、更にOutOfMemoryErrorも出てしまう次第です

    キャンセル

  • 2016/07/11 11:56

    解決しました。回答ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.12%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る