简体   繁体   中英

How can I resolve java.lang.OutOfMemoryError: Java heap space in the case when increasing the heap size will mean delayed OutOfMemoryError

I'm trying to build an application where a client sends its screen to the server, the client only sends its screen if there is a difference between last send screen and the latest captured screen(so that the program is easy on the network). And the server uses a JFrame and JLabel to display the image. But the thing is after a minute or two the server is giving a java.lang.OutOfMemoryError: Java heap space.

Please consider my code

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);

         }
       }
}

And here is my other piece of code, it is also showing the same problem, pls help me optimize it.

 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();
 }

Can anyone tell me if my logic for my purpose is correct or not and why am I getting the java.lang.OutOfMemoryError: Java Heap space and will extending the heap size help me as I'm planning for more than one client to be able to connect to the server?

Sorry if my question is dumb and any help will be appreciated.Thank you.

You keep adding the label to the frame. Shouldn't you just add it once?

There is an another problem: if((ImageIcon)is.readObject()!=null) will read out an image and lose it. You should instead keep it and not read it inside the if block. For instance:

if((imageIcon = (ImageIcon)is.readObject()) != null)

You need to manually signal for garbage collection after manually disposing of large objects. Also, general optimizing will help.

I would recommend (in your client):

  1. Create the frame
  2. Create a BufferedImage of sufficient size to draw incoming images.
  3. Add said BufferedImage to an ImageIcon
  4. Add said ImageIcon to your JLabel
  5. Add said JLabel to said frame
  6. Size your frame
  7. Display the frame

Then in your loop

  1. Read in an Image (only the Image, not an ImageIcon)
  2. Get the above BufferedImage's graphics context
  3. Draw the received image to said context.
  4. Erase pointer to image
  5. call System.gc();

See how that goes. Oh and if your inbound images are of varying size, you'll probably want to wipe the BufferedImage before drawing on it again else you get funky borders :-)

As for your server, it looks alright, I'd just ditch the ImageIcons and go with just passing plain old Images. Something like this:

Begin loop

  1. accept socket (as youre doing)
  2. get object output stream (as youre doing)
  3. get image1 (as youre doing)
  4. write image1

Begin inner loop

  1. get image2 (as youre doing)
  2. compare images (as youre doing)
  3. if(!b) (b==false works; it's just a somewhat odd way to write it)
  4. image1 = image2
  5. System.gc() (because that previous assignment removed the pointer to the old image1)
  6. write image1
  7. flush stream (as youre doing)

And if a perfectly clear image isnt manditory, you might also consider compressing the image before sending it, and just dealing with it as a byte array; you'd wire much less information.

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;
    }
}

and then just use

oos.writeObject(bufferedImageToJPEGBytes(image1)); //server side

and

image = jpegBytesToBufferedImage((byte[]) ois.readObject()); //client side

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM