プロが教えるわが家の防犯対策術!

表題のとおりソケット通信を行って、画面のスクリーンキャプチャをクライアントPCに送信するプログラムを考えています。それで、java.awt.Robotを使用することで画面のキャプチャをすることに成功しクライアントPCへ送信することにも成功したのですが、実はプログラムを立ち上げてから5,6回キャプチャ送信をするとjava.lang.OutOfMemoryErrorというエラーが発生しそれ以上送信することが出来なくなってしまいます。
なんとかどの部分でエラーが発生しているかは突き止めたのですが、そのエラーが発生する原因がどうしてもわかりません。JAVAを始めたばかりなので、もしかしたらエラーの発生部分も違うのかもしれませんが、どうかご教示ください。よろしくお願いいたします。

以下はエラーの発生するクラスです(エラー発生箇所にコメントを入れています)。
このクラスのgetScreen()メソッドを外部から呼び出すことでImageIconを取得しています。取得したImageIconはobjectInputStreamとobjectOutputStreamを使ったソケットを通してクライアントに送信しています。

import java.awt.*;
import java.awt.image.*;
import javax.swing.*;

public class KCapture {

private Robot rb;
private Image im;
private Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
private ImageIcon ii = new ImageIcon();
private Rectangle rc = new Rectangle(0,0,screenSize.width,screenSize.height);

public void KCapture(){

}

public void makeRobot(){
try{
rb = new Robot();
}
catch (AWTException awte){newJTF.setText(n+awte.toString());}
}

public ImageIcon getScreen(){
ii = new ImageIcon(((Image)rb.createScreenCapture(rc)));
//この一行上でjava.lang.OutOfMemoryErrorが発生します。
return ii;
}
}

A 回答 (2件)

ん~~。

わかんない。
スレッド関係にはあんまり自信がないので…。
(もっとも「スレッドには自信がある!」という人は世の中にそんなにいないと思う)

ただ、もしかしたら、
//入力監視を行います。
while(true){
ii = (ImageIcon)iStream.readObject();
newJL.setIcon(ii);
ii=null;
}
のnewJL.setIcon(ii);ところかなあ…。
というのは、これは独自に作ったスレッドからnewJL(JLabelですよね)の
setIconメソッドを使っているけど、
Swingコンポーネントのメソッドはほとんどスレッドセーフではありません。
つまり勝手に別のスレッドから使っちゃいけないことになっているのです。

http://java.sun.com/docs/books/tutorial/uiswing/ …

ただ「repaint()」は例外で、他のスレッドからも使えます。

だから、setIcon()を使わず、paintComponet()の中でdrawImage()を使って描画を行い、
更新はrepaint()で行うようにしてみたらどうでしょう。

それでだめなら、Imageの使用をやめてBufferedImageを使ってみるとか…。
(↑これは根拠のある意見ではありませんが、
ただのImageよりもBufferedImageの方が、普通の意味の「画像データ」に
近いと思うので)

あと、細かいことを言うと、上のwhile文の中で変数iiの使用は
必要がないので、できれば消した方がよいでしょう。
このコードには省略があって、実際には必要だという場合はすみません。

参考URL:http://java.sun.com/docs/books/tutorial/uiswing/ …
    • good
    • 0

前に生成したImageIconが、ガベージコレクションに回収されないで


メモリを圧迫しているのだと思われます。

上記のコード上だとiiは上書きされますが、getScreen()の返り値を使う側で
オブジェクトを殺してやらないと、ずっと残ってしまいます。
たとえば
AClass a, b;
a = new AClass();
b = a;
として、
a = null;
としても、作ったオブジェクト自体は解放されません。(bが参照を保持してるから)
ImageIconを使用する側のどこかに、参照が生きたままのところがあるのではないでしょうか。
なんとなく「ソケットを通して送信」の部分が怪しいように感じます。
    • good
    • 0
この回答へのお礼

ありがとうございす。非常に参考になりました!!

それで、ご指摘いただいたとおりガーベッジコレクションの拾えていない部分というのを自分なりに検討してみたのですが、どうもliar_adanさんの仰るとおり「ソケットを通して送信」の部分が怪しいような気がしています。
プログラムの流れは以下のようになっているのですが、まだエラーの原因がどうしてもよくわかりません。多分ストリームの記述方法に何か誤りがあるのではないかと勝手に思っているのですが、、、
何度も申し訳ありませんが、この部分でもし「これはおかしいだろ!」というような箇所があればご指摘いただけると幸いです。どうかよろしくお願いいたします。

」」」」」」」」」」」」」」」」」」」」」」」」」」」」」」」」」」
プログラムはJBuilder7Personalで作成しています。
クラスは4つから作成されていて一つ目はJbuilderが最初
に作るmain()のあるApplication1のクラス。
二つ目はmain()から呼び出されるGUIを定義しているFrame1の
クラス。
三つ目は先日の質問でも書かせていただいたクラスでKCaptureクラス
(これはスクリーンキャプチャを取得するクラスです)。
最後の4つ目がFrame1でソケット通信を行うためクライアント(サーバ)
のソケットを取得するthreadクラスです。
まずスクリーンキャプチャを送信する流れで最初に呼び出されるのは
KCaptureクラスのgetScreen()クラスで、これは先日の質問で書かせて
いただいたとおりです。
次にgetSceenの呼び出し下のFrame1では「送信」ボタンを押すと次の
プログラムが呼び出されます。

//(1)Frame1内の記述です。

ii = k.getScreen();
oStream.writeObject(ii);
oStream.flush();
ii =null;

//oStreamは
//private ObjectOutputStream oStream;
//Frame1のウィンドウオープン時にThreadクラスのgetOStream()
//を呼び出して取得しています。

---------------------------------------------------------------------

//(2)次にthread内の記述です。

public ObjectOutputStream getOStream(){
return oStream;
}

//Frame1からウィンドウオープン時に呼び出してFrame1のJLabelを
//newJLに参照渡しします。
public void setLabel(JLabel jl){
newJL = jl;
}
public void run(){
try{
servsock = new ServerSocket(3333);

sock = servsock.accept();
oStream = new ObjectOutputStream(sock.getOutputStream());
iStream = new ObjectInputStream(sock.getInputStream());

//入力監視を行います。
while(true){
ii = (ImageIcon)iStream.readObject();
newJL.setIcon(ii);
ii=null;
}
}
catch(Exception ee){
}


」」」」」」」」」」」」」」」」」」」」」」」」」」」」」」」」」」

書いてみて自分でも非常にわかりづらい説明のような気がします。
もうしわけありません。もし、不明な点、怪しい点などがございましたら
ご指摘ください。よろしくお願いいたします。

お礼日時:2003/02/20 12:14

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!