我正在尝试构建一个客户端将其屏幕发送到服务器的应用程序,如果上次发送屏幕和最新捕获的屏幕之间存在差异,则客户端仅发送其屏幕(以便程序在网络)。服务器使用JFrame和JLabel来显示图像。但事情是在一两分钟之后服务器提供java.lang.OutOfMemoryError:Java堆空间。
请考虑我的代码
public void go() throws Exception
{
s=new Socket("127.0.0.1",5000);
remoteIP = s.getInetAddress();
remoteIPOnly = remoteIP.toString().split("\\/");
frame=new JFrame(remoteIPOnly[1]);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
InputStream iss=s.getInputStream();
ObjectInputStream is=new ObjectInputStream(iss);
JLabel a=new JLabel();
while(!s.isClosed())
{
if((ImageIcon)is.readObject()!=null)
{
System.out.println("I got here");
imageIcon=(ImageIcon) is.readObject();
image=imageIcon.getImage();
rendered = null;
if (image instanceof RenderedImage)
{
rendered = (RenderedImage)image;
}
else
{
buffered = new BufferedImage(
imageIcon.getIconWidth(),
imageIcon.getIconHeight(),
BufferedImage.TYPE_INT_RGB
);
g = buffered.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
rendered = buffered;
}
frame.setSize(rendered.getWidth(),rendered.getHeight());
a.setIcon(imageIcon);
frame.add(a);
frame.setVisible(true);
}
}
}
这是我的另一段代码,它也显示了同样的问题,请帮助我优化它。
while(true)
{
s=serversocket.accept();
os=s.getOutputStream();
oss=new ObjectOutputStream(os);
image1=r.createScreenCapture(newRectangle(Toolkit.getDefaultToolkit().getScreenSize()));
imageicon=new ImageIcon(image1);
oss.writeObject(imageicon);
while(!s.isClosed()){
image2=r.createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit
().getScreenSize()));
b=checkIfImagesAreEqual(image1,image2);
System.out.println(b);
if(b==false){
image1=image2;
imageicon1=new ImageIcon(image2);
oss.writeObject(imageicon1);
oss.flush();
}
任何人都可以告诉我,为了我的目的我的逻辑是否正确以及为什么我得到java.lang.OutOfMemoryError:Java堆空间并扩展堆大小帮助我,因为我计划多个客户端能够连接到服务器吗?
很抱歉,如果我的问题很愚蠢,我们将不胜感激任何帮助。谢谢。
答案 0 :(得分:4)
您继续将标签添加到框架中。你不应该只添加一次吗?
还有一个问题:if((ImageIcon)is.readObject()!=null)
会读出一张图片并丢失它。您应该保留它而不是在if
块内读取它。例如:
if((imageIcon = (ImageIcon)is.readObject()) != null)
答案 1 :(得分:1)
手动处理大型对象后,需要手动发出垃圾回收信号。此外,一般优化将有所帮助。
我建议(在你的客户端):
然后在你的循环中
看看情况如何。哦,如果你的入站图片大小不一,你可能想要在再次绘制它之前擦除BufferedImage,否则你会得到时髦的边框: - )
至于你的服务器,它看起来还不错,我只是放弃了ImageIcons而只是传递普通的旧图像。像这样:
开始循环
开始内循环
如果完全清晰的图像不是强制性的,您也可以考虑压缩图像 在发送之前,只是将其作为字节数组处理;你输入的信息要少得多。
public static byte[] bufferedImageToJPEGBytes(BufferedImage bi){
try{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(bi, "jpg", baos);
return baos.toByteArray();
} catch (IOException e){
return null;
}
}
public static BufferedImage jpegBytesToBufferedImage(byte[] bytes){
try{
ByteArrayInputStream bais = new ByteArrayInputStream(rightImageBytes);
return ImageIO.read(bais);
} catch (IOException e){
return null;
}
}
然后只需使用
oos.writeObject(bufferedImageToJPEGBytes(image1)); //server side
和
image = jpegBytesToBufferedImage((byte[]) ois.readObject()); //client side