diff --git a/CMakeLists.txt b/CMakeLists.txt index caf42ebc0e..fc99f04129 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -834,12 +834,12 @@ if(BUILD_JAVA) if(ANDROID) include(cmake/android/OpenCVDetectAndroidSDK.cmake) else() - include(cmake/OpenCVDetectApacheAnt.cmake) - if(ANT_EXECUTABLE AND NOT OPENCV_JAVA_IGNORE_ANT) - ocv_update(OPENCV_JAVA_SDK_BUILD_TYPE "ANT") - elseif(NOT ANDROID) - find_package(Java) - if(Java_FOUND) + find_package(Java QUIET) + if(Java_FOUND) + include(cmake/OpenCVDetectApacheAnt.cmake) + if(ANT_EXECUTABLE AND NOT OPENCV_JAVA_IGNORE_ANT) + ocv_update(OPENCV_JAVA_SDK_BUILD_TYPE "ANT") + else() include(UseJava) ocv_update(OPENCV_JAVA_SDK_BUILD_TYPE "JAVA") endif() @@ -1997,7 +1997,7 @@ if(BUILD_JAVA) status(" Java:" Java_FOUND THEN "YES (ver ${Java_VERSION})" ELSE NO) status(" JNI:" JNI_INCLUDE_DIRS THEN "${JNI_INCLUDE_DIRS}" ELSE NO) endif() - status(" Java wrappers:" HAVE_opencv_java THEN "YES (${OPENCV_JAVA_SDK_BUILD_TYPE})" ELSE NO) + status(" Java wrappers:" HAVE_opencv_java THEN "YES (${OPENCV_JAVA_SDK_BUILD_TYPE})" ELSE NO) status(" Java tests:" BUILD_TESTS AND (opencv_test_java_BINARY_DIR OR opencv_test_android_BINARY_DIR) THEN YES ELSE NO) endif() diff --git a/cmake/android/OpenCVDetectAndroidSDK.cmake b/cmake/android/OpenCVDetectAndroidSDK.cmake index d9853ae4de..dd005e93cf 100644 --- a/cmake/android/OpenCVDetectAndroidSDK.cmake +++ b/cmake/android/OpenCVDetectAndroidSDK.cmake @@ -220,8 +220,10 @@ else() endif() # BUILD_ANDROID_PROJECTS if(ANDROID_PROJECTS_BUILD_TYPE STREQUAL "ANT") + ocv_update(OPENCV_JAVA_SDK_BUILD_TYPE "ANT") include(${CMAKE_CURRENT_LIST_DIR}/android_ant_projects.cmake) elseif(ANDROID_PROJECTS_BUILD_TYPE STREQUAL "GRADLE") + ocv_update(OPENCV_JAVA_SDK_BUILD_TYPE "GRADLE") include(${CMAKE_CURRENT_LIST_DIR}/android_gradle_projects.cmake) elseif(BUILD_ANDROID_PROJECTS) message(FATAL_ERROR "Internal error") diff --git a/modules/core/misc/java/src/java/core+Mat.java b/modules/core/misc/java/src/java/core+Mat.java index 8e98284dde..dff33760cd 100644 --- a/modules/core/misc/java/src/java/core+Mat.java +++ b/modules/core/misc/java/src/java/core+Mat.java @@ -4,14 +4,10 @@ import java.nio.ByteBuffer; // C++: class Mat //javadoc: Mat -public class Mat { - - public final long nativeObj; +public class Mat extends CleanableMat { public Mat(long addr) { - if (addr == 0) - throw new UnsupportedOperationException("Native object address is NULL"); - nativeObj = addr; + super(addr); } // @@ -20,7 +16,7 @@ public class Mat { // javadoc: Mat::Mat() public Mat() { - nativeObj = n_Mat(); + super(n_Mat()); } // @@ -29,7 +25,7 @@ public class Mat { // javadoc: Mat::Mat(rows, cols, type) public Mat(int rows, int cols, int type) { - nativeObj = n_Mat(rows, cols, type); + super(n_Mat(rows, cols, type)); } // @@ -38,7 +34,7 @@ public class Mat { // javadoc: Mat::Mat(rows, cols, type, data) public Mat(int rows, int cols, int type, ByteBuffer data) { - nativeObj = n_Mat(rows, cols, type, data); + super(n_Mat(rows, cols, type, data)); } // @@ -47,7 +43,7 @@ public class Mat { // javadoc: Mat::Mat(rows, cols, type, data, step) public Mat(int rows, int cols, int type, ByteBuffer data, long step) { - nativeObj = n_Mat(rows, cols, type, data, step); + super(n_Mat(rows, cols, type, data, step)); } // @@ -56,7 +52,7 @@ public class Mat { // javadoc: Mat::Mat(size, type) public Mat(Size size, int type) { - nativeObj = n_Mat(size.width, size.height, type); + super(n_Mat(size.width, size.height, type)); } // @@ -65,7 +61,7 @@ public class Mat { // javadoc: Mat::Mat(sizes, type) public Mat(int[] sizes, int type) { - nativeObj = n_Mat(sizes.length, sizes, type); + super(n_Mat(sizes.length, sizes, type)); } // @@ -74,7 +70,7 @@ public class Mat { // javadoc: Mat::Mat(rows, cols, type, s) public Mat(int rows, int cols, int type, Scalar s) { - nativeObj = n_Mat(rows, cols, type, s.val[0], s.val[1], s.val[2], s.val[3]); + super(n_Mat(rows, cols, type, s.val[0], s.val[1], s.val[2], s.val[3])); } // @@ -83,7 +79,7 @@ public class Mat { // javadoc: Mat::Mat(size, type, s) public Mat(Size size, int type, Scalar s) { - nativeObj = n_Mat(size.width, size.height, type, s.val[0], s.val[1], s.val[2], s.val[3]); + super(n_Mat(size.width, size.height, type, s.val[0], s.val[1], s.val[2], s.val[3])); } // @@ -92,7 +88,7 @@ public class Mat { // javadoc: Mat::Mat(sizes, type, s) public Mat(int[] sizes, int type, Scalar s) { - nativeObj = n_Mat(sizes.length, sizes, type, s.val[0], s.val[1], s.val[2], s.val[3]); + super(n_Mat(sizes.length, sizes, type, s.val[0], s.val[1], s.val[2], s.val[3])); } // @@ -101,12 +97,12 @@ public class Mat { // javadoc: Mat::Mat(m, rowRange, colRange) public Mat(Mat m, Range rowRange, Range colRange) { - nativeObj = n_Mat(m.nativeObj, rowRange.start, rowRange.end, colRange.start, colRange.end); + super(n_Mat(m.nativeObj, rowRange.start, rowRange.end, colRange.start, colRange.end)); } // javadoc: Mat::Mat(m, rowRange) public Mat(Mat m, Range rowRange) { - nativeObj = n_Mat(m.nativeObj, rowRange.start, rowRange.end); + super(n_Mat(m.nativeObj, rowRange.start, rowRange.end)); } // @@ -115,7 +111,7 @@ public class Mat { // javadoc: Mat::Mat(m, ranges) public Mat(Mat m, Range[] ranges) { - nativeObj = n_Mat(m.nativeObj, ranges); + super(n_Mat(m.nativeObj, ranges)); } // @@ -124,7 +120,7 @@ public class Mat { // javadoc: Mat::Mat(m, roi) public Mat(Mat m, Rect roi) { - nativeObj = n_Mat(m.nativeObj, roi.y, roi.y + roi.height, roi.x, roi.x + roi.width); + super(n_Mat(m.nativeObj, roi.y, roi.y + roi.height, roi.x, roi.x + roi.width)); } // @@ -754,12 +750,6 @@ public class Mat { return new Mat(n_zeros(sizes.length, sizes, type)); } - @Override - protected void finalize() throws Throwable { - n_delete(nativeObj); - super.finalize(); - } - // javadoc:Mat::toString() @Override public String toString() { @@ -1834,9 +1824,6 @@ public class Mat { // C++: static Mat Mat::zeros(int ndims, const int* sizes, int type) private static native long n_zeros(int ndims, int[] sizes, int type); - // native support for java finalize() - private static native void n_delete(long nativeObj); - private static native int nPutD(long self, int row, int col, int count, double[] data); private static native int nPutDIdx(long self, int[] idx, int count, double[] data); diff --git a/modules/java/generator/CMakeLists.txt b/modules/java/generator/CMakeLists.txt index 130e6d5fec..fb1f159e71 100644 --- a/modules/java/generator/CMakeLists.txt +++ b/modules/java/generator/CMakeLists.txt @@ -62,6 +62,24 @@ ocv_bindings_generator_populate_preprocessor_definitions( opencv_preprocessor_defs ) +if(OPENCV_JAVA_CLEANING_API) + if(OPENCV_JAVA_CLEANING_API STREQUAL "finalize") + set(opencv_supported_cleaners "false") + elseif(OPENCV_JAVA_CLEANING_API STREQUAL "cleaner") + set(opencv_supported_cleaners "true") + else() + message(FATAL_ERROR "OPENCV_JAVA_CLEANING_API should be one of \"finalize\" or \"cleaner\"") + endif() +else() + if(ANDROID OR (Java_VERSION VERSION_LESS 9)) + message(STATUS "Set Cleaners to False") + set(opencv_supported_cleaners "false") + else() + message(STATUS "Set Cleaners to True") + set(opencv_supported_cleaners "true") + endif() +endif(OPENCV_JAVA_CLEANING_API) + set(CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/gen_java.json") set(__config_str "{ @@ -74,7 +92,8 @@ ${opencv_preprocessor_defs} }, \"files_remap\": [ ${__remap_config} - ] + ], + \"support_cleaners\": ${opencv_supported_cleaners} } ") if(EXISTS "${CONFIG_FILE}") diff --git a/modules/java/generator/gen_java.py b/modules/java/generator/gen_java.py index 0885969277..591eb38ea3 100755 --- a/modules/java/generator/gen_java.py +++ b/modules/java/generator/gen_java.py @@ -23,6 +23,7 @@ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) # list of modules + files remap config = None ROOT_DIR = None +USE_CLEANERS = True FILES_REMAP = {} def checkFileRemap(path): path = os.path.realpath(path) @@ -366,6 +367,7 @@ class ClassInfo(GeneralInfo): module = m, name = self.name, jname = self.jname, + jcleaner = "long nativeObjCopy = nativeObj;\n org.opencv.core.Mat.cleaner.register(this, () -> delete(nativeObjCopy));" if USE_CLEANERS else "", imports = "\n".join(self.getAllImports(M)), docs = self.docstring, annotation = "\n" + "\n".join(self.annotation) if self.annotation else "", @@ -948,6 +950,7 @@ class JavaWrapperGenerator(object): tail = ")" else: ret_val = "nativeObj = " + tail = ";\n long nativeObjCopy = nativeObj;\n org.opencv.core.Mat.cleaner.register(this, () -> delete(nativeObjCopy))" if USE_CLEANERS else "" ret = "" elif self.isWrapped(ret_type): # wrapped class constructor = self.getClass(ret_type).jname + "(" @@ -1214,8 +1217,9 @@ JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_${clazz}_$fname ci.cpp_code.write("\n".join(fn["cpp_code"])) if ci.name != self.Module or ci.base: - # finalize() - ci.j_code.write( + # finalize() for old Java + if not USE_CLEANERS: + ci.j_code.write( """ @Override protected void finalize() throws Throwable { @@ -1225,7 +1229,7 @@ JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_${clazz}_$fname ci.jn_code.write( """ - // native support for java finalize() + // native support for java finalize() or cleaner private static native void delete(long nativeObj); """ ) @@ -1233,7 +1237,7 @@ JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_${clazz}_$fname ci.cpp_code.write( """ // -// native support for java finalize() +// native support for java finalize() or cleaner // static void %(cls)s::delete( __int64 self ) // JNIEXPORT void JNICALL Java_org_opencv_%(module)s_%(j_cls)s_delete(JNIEnv*, jclass, jlong); @@ -1454,6 +1458,12 @@ if __name__ == "__main__": FILES_REMAP = { os.path.realpath(os.path.join(ROOT_DIR, f['src'])): f['target'] for f in config['files_remap'] } logging.info("\nRemapped configured files (%d):\n%s", len(FILES_REMAP), pformat(FILES_REMAP)) + USE_CLEANERS = config['support_cleaners'] + if (USE_CLEANERS): + logging.info("\nUse Java 9+ cleaners\n") + else: + logging.info("\nUse old style Java finalize()\n") + dstdir = "./gen" jni_path = os.path.join(dstdir, 'cpp'); mkdir_p(jni_path) java_base_path = os.path.join(dstdir, 'java'); mkdir_p(java_base_path) @@ -1543,6 +1553,17 @@ if __name__ == "__main__": preprocessor_definitions) else: logging.info("No generated code for module: %s", module) + + # Copy Cleaner / finalize() related files + if USE_CLEANERS: + cleaner_src = os.path.join(SCRIPT_DIR, "src", "java9", "CleanableMat.java") + else: + cleaner_src = os.path.join(SCRIPT_DIR, "src", "java_classic", "CleanableMat.java") + + cleaner_dst = os.path.join(java_base_path, "org", "opencv", "core", "CleanableMat.java") + print("cleaner_dst: ", cleaner_dst) + copyfile(cleaner_src, cleaner_dst) + generator.finalize(jni_path) print('Generated files: %d (updated %d)' % (total_files, updated_files)) diff --git a/modules/java/generator/src/cpp/CleanableMat.cpp b/modules/java/generator/src/cpp/CleanableMat.cpp new file mode 100644 index 0000000000..a1110cd391 --- /dev/null +++ b/modules/java/generator/src/cpp/CleanableMat.cpp @@ -0,0 +1,28 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#include "opencv2/core.hpp" + +#define LOG_TAG "org.opencv.core.CleanableMat" +#include "common.h" +#include + +using namespace cv; + +extern "C" { + // + // native support for java finalize() or cleaners + // static void CleanableMat::n_delete( __int64 self ) + // + + JNIEXPORT void JNICALL Java_org_opencv_core_CleanableMat_n_1delete + (JNIEnv*, jclass, jlong self); + + JNIEXPORT void JNICALL Java_org_opencv_core_CleanableMat_n_1delete + (JNIEnv*, jclass, jlong self) + { + // LOGD("CleanableMat.n_delete() called\n"); + delete (Mat*) self; + } +} diff --git a/modules/java/generator/src/cpp/Mat.cpp b/modules/java/generator/src/cpp/Mat.cpp index f43a4c6ed5..128a4fc08a 100644 --- a/modules/java/generator/src/cpp/Mat.cpp +++ b/modules/java/generator/src/cpp/Mat.cpp @@ -2114,22 +2114,6 @@ JNIEXPORT jlong JNICALL Java_org_opencv_core_Mat_n_1zeros__I_3II return 0; } - - -// -// native support for java finalize() -// static void Mat::n_delete( __int64 self ) -// - -JNIEXPORT void JNICALL Java_org_opencv_core_Mat_n_1delete - (JNIEnv*, jclass, jlong self); - -JNIEXPORT void JNICALL Java_org_opencv_core_Mat_n_1delete - (JNIEnv*, jclass, jlong self) -{ - delete (Mat*) self; -} - } // extern "C" namespace { diff --git a/modules/java/generator/src/java9/CleanableMat.java b/modules/java/generator/src/java9/CleanableMat.java new file mode 100644 index 0000000000..29677ffa3c --- /dev/null +++ b/modules/java/generator/src/java9/CleanableMat.java @@ -0,0 +1,23 @@ +package org.opencv.core; + +import java.lang.ref.Cleaner; + +public abstract class CleanableMat { + // A native memory cleaner for the OpenCV library + public static Cleaner cleaner = Cleaner.create(); + + protected CleanableMat(long obj) { + if (obj == 0) + throw new UnsupportedOperationException("Native object address is NULL"); + + nativeObj = obj; + + // The n_delete action must not refer to the object being registered. So, do not use nativeObj directly. + long nativeObjCopy = nativeObj; + cleaner.register(this, () -> n_delete(nativeObjCopy)); + } + + private static native void n_delete(long nativeObj); + + public final long nativeObj; +} diff --git a/modules/java/generator/src/java_classic/CleanableMat.java b/modules/java/generator/src/java_classic/CleanableMat.java new file mode 100644 index 0000000000..1909483e7c --- /dev/null +++ b/modules/java/generator/src/java_classic/CleanableMat.java @@ -0,0 +1,21 @@ +package org.opencv.core; + +public abstract class CleanableMat { + + protected CleanableMat(long obj) { + if (obj == 0) + throw new UnsupportedOperationException("Native object address is NULL"); + + nativeObj = obj; + } + + @Override + protected void finalize() throws Throwable { + n_delete(nativeObj); + super.finalize(); + } + + private static native void n_delete(long nativeObj); + + public final long nativeObj; +} diff --git a/modules/java/generator/templates/java_class.prolog b/modules/java/generator/templates/java_class.prolog index 5662534bc5..e3a2822277 100644 --- a/modules/java/generator/templates/java_class.prolog +++ b/modules/java/generator/templates/java_class.prolog @@ -9,7 +9,10 @@ $docs$annotation public class $jname { protected final long nativeObj; - protected $jname(long addr) { nativeObj = addr; } + protected $jname(long addr) { + nativeObj = addr; + $jcleaner + } public long getNativeObjAddr() { return nativeObj; }