Home > front end >  java OutOfMemoryError: memory usage is too high: physicalBytes > maxPhysicalBytes javacpp Pointer
java OutOfMemoryError: memory usage is too high: physicalBytes > maxPhysicalBytes javacpp Pointer

Time:01-12

I am having this issue:

java.lang.OutOfMemoryError: Physical memory usage is too high: physicalBytes = 1G > maxPhysicalBytes = 1G at org.bytedeco.javacpp.Pointer.deallocator(Pointer.java:562)

long Pointer.physicalBytes is incrementing even if we deallocate every single Pointer object and call GC - I've been monitoring JVM heap size and it is under control, never over 20% usage, which means the deallocation is well executed, but for some reason the information (real thing) is not being passed over to Poniter.physicalBytes (which never gets decreased) and it throws the error by mistake when it reaches the value of Pointer.maxPhysicalBytes

Looks like this was fixed a few weeks ago (https://github.com/bytedeco/javacpp-presets/issues/423), but I am still having this issue even after getting the latest version of JavaCPP (1.3.3)

This is my code:

import static org.bytedeco.javacpp.opencv_core.cvClearMemStorage;
import static org.bytedeco.javacpp.opencv_core.cvGetSeqElem;
import static org.bytedeco.javacpp.opencv_core.cvPoint;
import static org.bytedeco.javacpp.opencv_core.cvSize;
import static org.bytedeco.javacpp.opencv_imgproc.CV_AA;
import static org.bytedeco.javacpp.opencv_imgproc.cvRectangle;
import static org.bytedeco.javacpp.opencv_objdetect.cvHaarDetectObjects;

import org.bytedeco.javacpp.BytePointer;
import org.bytedeco.javacpp.Pointer;
import org.bytedeco.javacpp.opencv_core;
import org.bytedeco.javacpp.opencv_core.CvMemStorage;
import org.bytedeco.javacpp.opencv_core.CvPoint;
import org.bytedeco.javacpp.opencv_core.CvRect;
import org.bytedeco.javacpp.opencv_core.CvScalar;
import org.bytedeco.javacpp.opencv_core.CvSeq;
import org.bytedeco.javacpp.opencv_core.CvSize;
import org.bytedeco.javacpp.opencv_core.IplImage;
import org.bytedeco.javacpp.opencv_objdetect.CascadeClassifier;
import org.bytedeco.javacpp.opencv_objdetect.CvHaarClassifierCascade;
(...)    

public class ObjectDetection {

    private static CvMemStorage storage = CvMemStorage.create();

    (...)

    public static synchronized Detection detect(IplImage src, Configuration cfg) {

        CvMemStorage storage = CvMemStorage.create();
        CvSeq sign = cvHaarDetectObjects(src, cfg.cascade, storage, cfg.scale, cfg.neighbors, cfg.method.getVal(), cfg.minSize, cfg.maxSize);

        int total_objs = sign.total();

        for (int i = 0; i < total_objs; i  ) {
            BytePointer seqElem = cvGetSeqElem(sign, i);
            CvRect r = new CvRect(seqElem);
            CvPoint p1 = cvPoint(r.x(), r.y());
            CvPoint p2 = cvPoint(r.width()   r.x(), r.height()   r.y());
            cvRectangle(src, p1, p2, CvScalar.RED, 2, CV_AA, 0);
            p1.deallocate();
            p2.deallocate();
            r.close();
            r.deallocate();
            seqElem.deallocate();
        }

        BufferedImage img = Images.toBufferedImage(src);

        sign.deallocate();
        src.deallocate();
        storage.deallocate();

        Pointer.deallocateReferences();

        return new Detection(img, total_objs);
    }

    public static class Detection{
        private BufferedImage img;
        private int count;
        private Detection(BufferedImage i, int c){
            img = i; count = c;
        }
        public BufferedImage getImage(){
            return img;
        }
        public int getObjectsCount(){
            return count;
        }
    }


    public static class Configuration{
        private String configName;
        private CascadeClassifier xmlFile;
        private CvHaarClassifierCascade cascade;
        private CvSize minSize; 
        private CvSize maxSize;
        private double scale;
        private int neighbors;
        private Method method;

        private Configuration(String configuration) throws JSONException, IOException{
            configName = configuration;
            JSONObject cfg = new JSONObject(new JSONTokener(new FileReader(new File(configuration ".cfg"))));
            scale = cfg.getDouble("scale");
            neighbors = cfg.getInt("neighbors");
            method = Method.valueOf(cfg.getString("method"));
            int min = cfg.getInt("min_size");
            int max = cfg.getInt("max_size");
            minSize = cvSize(min,min);
            maxSize = cvSize(max,max);
            xmlFile = new CascadeClassifier(configuration ".xml");
            cascade = new CvHaarClassifierCascade(xmlFile.getOldCascade());
        }

        public void dealocate(){
            xmlFile.deallocate();
            cascade.deallocate();
            minSize.deallocate();
            maxSize.deallocate();
            configs.remove(configName);
        }

    }
}

and this is the stacktrace:

java.lang.OutOfMemoryError: Physical memory usage is too high: physicalBytes = 1G > maxPhysicalBytes = 1G
org.bytedeco.javacpp.Pointer.deallocator(Pointer.java:576)
org.bytedeco.javacpp.Pointer.init(Pointer.java:121)
org.bytedeco.javacpp.opencv_core.cvPoint(Native Method)
br.com.irisbot.visualrecognition.haarcascade.ObjectDetection.detect(ObjectDetection.java:83)
br.com.irisbot.visualrecognition.haarcascade.ObjectDetectionServlet.doPost(ObjectDetectionServlet.java:71)
javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:475)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:651)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:500)
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:754)
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1376)
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
java.lang.Thread.run(Thread.java:748)

CodePudding user response:

In fact, after a long time struggling with the memory issue, and with a serious requirement to have a production service (that does not hang after some requests) I worked around the problem:

  1. create a stand alone app (runnable Jar) to perform the OpenCV procedure and returns the result to the console
  2. create a web service that calls the app as a Java Process on the OS command line (which forces it to run in a separate JVM)
  3. capture the results from the process' console
  4. return the results through the web service

You may find it not so elegant, I agree, but at the end of the day it WORKS! and does not perform so bad at all ;-)

CodePudding user response:

There are most likely some objects that are not getting deallocated properly. Try to use PointerScope to more easily capure them all: http://bytedeco.org/news/2018/07/17/bytedeco-as-distribution/

Here is a minimal example demonstrating how to use it with, for example, CascadeClassifier:

import org.bytedeco.javacpp.*;
import org.bytedeco.javacv.*;
import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_imgproc.*;
import static org.bytedeco.javacpp.opencv_objdetect.*;

public class PointerScopeDemo {
    public static void main(String[] args) throws Exception {
        CascadeClassifier classifier = new CascadeClassifier("haarcascade_frontalface_alt2.xml");
        FrameGrabber grabber = FrameGrabber.createDefault(0);
        grabber.start();

        Mat image;
        OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();
        while ((image = converter.convert(grabber.grab())) != null) {
            try (PointerScope scope = new PointerScope()) {
                RectVector faces = new RectVector();
                classifier.detectMultiScale(image, faces);
                System.out.println(faces.size());
            }
        }
    }
}
  • Related