ошибка нехватки памяти java при отправке непрерывных изображений веб-камеры

#java #serversocket #objectinputstream

#java #serversocket #objectinputstream

Вопрос:

Я пытался отправить непрерывные изображения с веб-камеры (используя Webcam-api) от клиента через ObjectOutStream на сервер и показать его на ярлыке кадра и нашел код на youtube, но через некоторое время программа выдает ошибку «из памяти». Кто-нибудь может объяснить ее причину? Может быть, InputStream на стороне сервера хранит все изображения, которые не требуются. Если это так, предложите какие-либо методы очистки InputStream на сервере.

TestClient :

 import java.io.IOException;

import java.awt.image.BufferedImage;
import java.io.*;
import java.net.*;

import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

import com.github.sarxos.webcam.Webcam;
import com.github.sarxos.webcam.WebcamResolution;

public class TestClient {


    static Socket socket;
    public static void main(String[] args) throws IOException,ClassNotFoundException{
        Webcam webcam = Webcam.getDefault();
        webcam.setViewSize(WebcamResolution.VGA.getSize());
        System.out.println(WebcamResolution.VGA.getSize());
        webcam.open();

        socket = new Socket("localhost",5001);
        
        ObjectOutputStream dout = new ObjectOutputStream(socket.getOutputStream());
        JLabel label = new JLabel();
        JFrame frame = new JFrame("Client");

        frame.setSize(WebcamResolution.VGA.getSize());
        frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
        label.setSize(WebcamResolution.VGA.getSize());
        label.setVisible(true);

        frame.add(label);
        frame.setVisible(true);


        while(true) {
            BufferedImage wc = webcam.getImage();
            ImageIcon ic = new ImageIcon(wc);
            label.setIcon(ic);
            dout.writeObject(ic);
            dout.flush();
        }

    }
}
  

Тестовый сервер:

 import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;

import javax.swing.*;

public class TestServer {
    public static void main(String[] args) throws IOException,ClassNotFoundException{
        ServerSocket server = new ServerSocket(5001);
        System.out.println("Waiting...");
        Socket socket = server.accept();
        System.out.println("Connected ..");
        ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
        JLabel label = new JLabel();
        JFrame frame = new JFrame("ServerSide");
        
        frame.setSize(640,480);
        frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
        label.setSize(640,480);
        label.setVisible(true);
        
        frame.add(label);
        frame.setVisible(true);
        
        while(true) {
            label.setIcon((ImageIcon)in.readObject());
        }
            
    }
    

}
  

Ошибка через некоторое время::

 Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.base/java.lang.reflect.Array.newArray(Native Method)
    at java.base/java.lang.reflect.Array.newInstance(Array.java:78)
    at java.base/java.io.ObjectInputStream.readArray(ObjectInputStream.java:2036)
    at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1656)
    at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:482)
    at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:440)
    at java.desktop/javax.swing.ImageIcon.readObject(ImageIcon.java:501)
    at java.base/jdk.internal.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at java.base/java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1175)
    at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2295)
    at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2166)
    at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1668)
    at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:482)
    at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:440)
    at TestServer.main(TestServer.java:29)
  

Редактировать::
Я пробовал это, но сервер получает изображение только в первый раз, после чего возникает исключение нулевой точки. Я не могу понять, почему?

Клиент:

 DataOutputStream out = new DataOutputStream(socket.getOutputStream());
    
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while(true) {
   BufferedImage wc = webcam.getImage();
   ImageIO.write(wc, "jpg", bos); 
   byte imgBytes[] = bos.toByteArray();
   out.write(imgBytes,0,imgBytes.length);
   bos.flush();
}
  

Сервер ::

 DataInputStream in=new DataInputStream(socket.getInputStream());
ImageInputStream imgin = ImageIO.createImageInputStream(in);

while(true){
    BufferedImage img=ImageIO.read(imgin);
    ImageIcon ic = new ImageIcon(img);
    label.setIcon(ic);
}
  

Комментарии:

1. Попробуйте добавить a Thread.sleep в любой из циклов (либо на клиенте, либо на сервере) и посмотрите, что произойдет

Ответ №1:

Ни InputStream, ни ObjectInputStream не могут быть «очищены», и они не «сохраняют» какие-либо предыдущие результаты. Скорее всего, биты на проводе повреждены, и отправляемые данные заставляют операцию чтения пытаться выделить гигантский массив «случайно».

Обратите внимание, что отправка ImageIcons через ObjectOutputStream невероятно неэффективна; протокол тратит тонны пропускной способности и тратит много циклов процессора, пытаясь сериализовать этот объект, который не был предназначен для этого. Лучше сохранить ваше изображение в какой-нибудь быстрый формат изображения (возможно, не PNG) и отправить его, что попутно также значительно упростит отладку поврежденных отправлений.

Комментарии:

1. Это не может быть правдой: Most likely the bits on the wire are corrupted, and the data being sent is causing the read operation to try to allocate a gigantic array 'by accident'. как могут быть повреждены биты на проводе? Если бы это было так, Интернет никогда бы не работал. Существуют определенные проверки, начиная с сериализации / десериализации java и заканчивая протоколом TCP и так далее. Почти на всех уровнях есть некоторые проверки, чтобы предотвратить это.

2. Проблемы с версиями между классами (serialVersionUID затем переопределяет проверку этого), например.

3. Но проблемы с версиями приводят к исключениям во время выполнения, они не приводят к ошибкам нехватки памяти, не так ли?

4. Они делают. Например, если существуют разные версии объекта, но установлен sVUID (обычная практика), происходит смещение поля.

Ответ №2:

То, что вы здесь делаете, постоянно устанавливает значок метки:

 while(true) {
            label.setIcon((ImageIcon)in.readObject());
        }
  

То, что делает этот код, создает новые экземпляры ImageIcon , ссылку label на этот экземпляр, а затем, когда приходит новое изображение, ссылка на исходный объект теряется, и сборщик мусора не может очистить его или не получает возможности очистить его, поскольку это происходит в цикле без каких-либо пауз.

Таким образом, если камера непрерывно генерирует 60 кадров в секунду, у сборщика мусора не будет возможности очистить память, поскольку (гипотетически) в памяти будет создаваться 60 объектов в секунду.

Вы можете отслеживать свое приложение с помощью jvisualvm и видеть, что это так.

Комментарии:

1. Ничто из этого не соответствует действительности. in.readObject() заканчивает замораживанием этого потока (поскольку, в конечном итоге, вы попадаете в сетевой сокет, который блокируется), и в этот момент сборщик мусора сработает просто отлично. Даже если поток никогда не блокируется, он будет GC просто отлично; как только вы запросите у виртуальной машины выделение, которое она не может вам предоставить, оно (обычно, это сложно) сначала GC.

2. Я не говорю, что это на 100% верно, но это моя теория. Он не остановится в сокете, если есть объекты, доступные для чтения.

3. Ваша теория основана на неправильных представлениях о том, как работает виртуальная машина. Это не может быть правдой.

4. Я попытался добавить system.gc(); в каждом цикле он все равно зависал.

5. .. это потому, что этот ответ не имеет смысла, @AAYUSHSHANDILYA