为什么 Java 应用程序在处理 x 个文件后崩溃?

Why does Java app crash after processing x number of files?

我只是在万不得已的时候问这个问题。我被难住了。

我编写了一个执行非常简单的图像处理的小应用程序。它是使用 JDK 1.8 制作的,用 Netbeans 8.0.1 编写,在 Debian Linux.

上是 运行

应用程序通过进程构建器调用 'streamer' 网络摄像头程序,以用户设置的特定帧速率捕获大量单个帧。一旦开始捕获,它就会开始将帧转换为 RGB 值并检查是否有任何像素高于用户定义的阈值。如果没有像素超过此阈值,它只会删除该帧。如果有任何像素超过它,它会将框架移动到不同的文件夹以供用户检查。这一切都很好。它跟上什至相对较高的帧率,并按预期选择合适的帧。但是,当处理的帧数达到 1500 左右(对于较低的帧率或更少)时,程序会冻结。我已经尝试在命令行手动向 streamer 发出命令,它似乎完全能够生成所需数量的流媒体,所以我不得不假设问题出在我的编码上。图片很小 (320x240)。我是否以某种方式最大化可用内存(我没有收到任何错误,只是冻结)。

这个程序的目的是检测宇宙射线对 CMOS 传感器的影响,这是朋友论文的一部分。如果我不能让它可靠地工作,那可怜的孩子将不得不手动检查录像!

代码附在下面。抱歉代码太长了,但由于所有代码都相当重要,所以我不想省略任何内容。

package cosmicraysiii;

import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;

public class CosmicRaysIII {

    public static int dark = 0;//Dark current determined by averaging
    public static int tol = 0;//Tolerance set by user
    public static int frames = 0;//Total number of frames set by user
    public static int runs = 0;
    public static int rate = 0;//Framerate set by user
    public static int time = 0;//Total time calculated
    public static String zeros = "";
    public static int ready = 0;

    public static void main(String[] args) throws IOException,     InterruptedException {

        //Get directory ID from user
        String id = JOptionPane.showInputDialog("Enter a folder name for detections (no ., in name):");

        //Get dark current
        Dark d = new Dark();
        dark = d.getCurrent();

        //Get tolerance from user, will be added to dark current.
        String t = JOptionPane.showInputDialog("Dark Current = " + dark + "/255\n"
            + "Enter a tolerance (integer values only).\n "
            + "This will be added to the dark current:");
        tol = Integer.parseInt(t) + dark;

        //Get number of frames from user
        String fs = JOptionPane.showInputDialog("Enter the total number of frames required (Mulitples of 500 only):");
        frames = Integer.parseInt(fs);
        runs = frames / 500;

        //Get framerate from user
        String r = JOptionPane.showInputDialog("Enter the framerate required:");
        rate = Integer.parseInt(r);

        //Determine duration
        time = (int) Math.round(frames / rate);

        //Provide summary for user and request permission to continue
        int secs = time % 60;
        int mins = (time - secs) / 60;
        int hrs = (mins - (mins % 60)) / 60;
        if (hrs >= 1) {
            mins = mins % 60;
        }
        String theMessage = "The following parameters have been set:\n"
                + "Tolerance (including dark current): " + tol + "\n"
                + "Frames: " + frames + "\n"
                + "Frame rate: " + rate + " fps\n"
                + "Total capture time: " + time + " sec\n"
                + "                  " + hrs + " h " + mins + " m " + secs + " s\n"
                + "\n"
                + "Would you like to proceed?";
        int result = JOptionPane.showConfirmDialog(null, theMessage, "Continue?", JOptionPane.OK_CANCEL_OPTION);

        if (result == 2) {
            System.exit(0);
        }

        //Create directory for data acquisition
        ProcessBuilder pb1 = new ProcessBuilder("mkdir", "data");
        pb1.start();

        //Establish array of filenames
        String[] filenames = new String[frames];

        //Fill filenames array with filenames
        //Taking into consideration that the filename 
        //will have a varying number of zeros appended
        //before the frame number, dependent on the 
        //order of the frame number
        for (int i = 0; i < frames; i++) {
             if (i < 10) {
                 zeros = "00000000";
             } else if (i >= 10 && i < 100) {
                 zeros = "0000000";
             } else if (i >= 100 && i < 1000) {
                 zeros = "000000";
             } else if (i >= 1000 && i < 10000) {
                 zeros = "00000";
             } else if (i >= 10000 && i < 100000) {
                 zeros = "0000";
             } else if (i >= 100000 && i < 1000000) {
                 zeros = "000";
             } else if (i >= 1000000 && i < 10000000) {
                 zeros = "00";
            } else if (i >= 10000000 && i < 100000000) {
                zeros = "0";
            } else {
                zeros = "";
            }
            filenames[i] = "./data/frame" + zeros + i + ".ppm";
        }

        //Begin data acquisition
        new Thread(new Runnable() {
            public void run() {
                try {

                    //Capture images
                    ProcessBuilder pb2 = new ProcessBuilder("streamer", "-t", Integer.toString(frames), "-r", Integer.toString(rate), "-p", "0", "-o", "./data/frame000000000.ppm");
                    Process p = pb2.start();
                    p.waitFor();
                    ready = 1;
                } catch (IOException | InterruptedException ex) {
                    Logger.getLogger(CosmicRaysIII.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }).start();

        //Sleep to allow some image capture to prevent thread disordering
        Thread.sleep(3000);

        //Check array size
        System.out.println("Array size: " + filenames.length);

        //Conduct image analysis
        new Thread(new Runnable() {
            public void run() {
                int done = 0;
                int donea = 0;
                while (ready == 0) {
                    for (int i = 0; i < frames; i++) {
                        File f = new File(filenames[i]);
                        if (f.exists() && !filenames[i].equals(""))     {//Check file still exists
                            try {
                                 //Perform analysis steps
                                 Analysis a = new Analysis();

                                //STEP 1: Convert file from P6 to P3
                                String newfile = a.convert(filenames[i], zeros, i);
                                //STEP 2: Read file
                                a.read(newfile, tol, i, id);
                                filenames[i] = "";
                                done++;

                            } catch (IOException ex) {
                                Logger.getLogger(CosmicRaysIII.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        }
                        if (done > donea) {
                            System.out.println(done + " files processed");
                            donea = done;
                        }
                    }
                 }
             }
        }).start();

    }

}

Analyse.javaclass如下:

package cosmicraysiii;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class Analysis {

    public String convert(String ofile, String zeros, int number) throws IOException {
        //Create new file name
        String nfile = "./proc/frame" + zeros + number + ".ppm";

        //Ensure process directory exists
        ProcessBuilder mkdir = new ProcessBuilder("mkdir", "proc");
        mkdir.start();

        //Convert file to P3 PPM (RGB format) and move to process folder
        ProcessBuilder convert = new ProcessBuilder("convert", ofile, "-compress", "none", nfile);
        convert.start();

        //Delete original file
        ProcessBuilder del = new ProcessBuilder("sudo", "rm", ofile);
        del.start();

        //Return new filename
        return nfile;
    }

    public void read(String filename, int tol, int ix, String id) throws     FileNotFoundException, IOException {

        int move = 0;

        //Make directory for hits
        ProcessBuilder mkdir = new ProcessBuilder("mkdir", "hits" + id);
        mkdir.start();

        //Open reader to read file
        File f = new File(filename);
        if (f.exists()) {
            BufferedReader br = new BufferedReader(new FileReader(filename));
            String line;

            //To eliminate header
            int x = 0;

            //Iterate through text to find abnormal pixels
            while ((line = br.readLine()) != null) {
                x++;
                String[] pixrgb = line.split("\ ");

                //Iterate through pixels on each line
                for (int i = 0; i < pixrgb.length; i++) {
                    if (x >= 4) {//Eliminate header

                        //Check each pixel value
                         try {
                             int pixval = Integer.parseInt(pixrgb[i]);
                             if (pixval > tol) {
                                move = 1;
                                break;
                            }
                        } catch (NumberFormatException ne) {

                        }
                    }
                }

            }
            if (move == 1) {
                 //Move file to hits folder
                 ProcessBuilder pb3 = new ProcessBuilder("sudo", "cp", filename, "./hits" + id + "/detection" + ix + ".ppm");
                pb3.start();

                //Delete original file
                ProcessBuilder del = new ProcessBuilder("sudo", "rm", filename);
                del.start();
            }
        }
                //Delete original file
                ProcessBuilder del = new ProcessBuilder("sudo", "rm", filename);
                del.start();
    }
}

我很感激要发布的代码太长了。非常感谢您提供的任何帮助。

G

好的,我已经通过彻底检查分析过程解决了这个问题。首先,我没有将图像转换为 P3 .ppm 文件,而是使用 BufferedReader 直接检查图像中的像素。然后,我停止反复循环文件列表。相反,调用 Analysis() 的循环仅 运行 遍历文件名列表一次。如果它遇到一个尚不存在的文件,它会执行 Thread.sleep(500) 然后再试一次。我现在已经成功地 运行 一批 50,000 帧而没有发生任何事故,并且由于改进的过程,现在内存消耗少了很多。只是想我应该把这个答案放在这里以防万一有人遇到它。如果有人需要,我可以 post 编码。