C ++中的内存泄漏,其中Java是通过JNI调用的

时间:2017-09-27 10:35:08

标签: java c++ c memory-leaks java-native-interface

需要从C ++项目调用Java项目。所以我写了包装器。不幸的是我的主要语言是Java,而且我在C或C ++中没有做任何事情。似乎我在代码中的某处发生了内存泄漏,但我不知道在哪里。

这是包装器的代码:

#include <cstdint>
#include <jni.h>
#include <stdio.h>
#include <string>
#include <vector>
#include <memory>
#include "wrapperJava.h"
#include <stdint.h>

using namespace std;

JNIEnv* env;
jmethodID javaMethodCreate;
jclass javaClass;
int init = 0;
jint rate = 16000;
jint channels = 1;
jint bits = 16;

JNIEnv* create_vm() 
{
    JavaVM* jvm;
    JNIEnv* env;
    JavaVMInitArgs args;
    JavaVMOption options[2];

    args.version = JNI_VERSION_1_8;
    args.nOptions = 2;
    options[0].optionString = "-Djava.class.path=./router-core-0.0.1-SNAPSHOT.jar:./cp:cp/antlr-2.7.7.jar:cp/aopalliance-1.0.jar:cp/asm-3.3.1.jar:cp/aspectjweaver-1.8.9.jar:cp/bcprov-jdk15on-1.51.jar:cp/cdi-api-1.1.jar:cp/classmate-1.3.3.jar:cp/commons-codec-1.10.jar:cp/commons-io-2.5.jar:cp/commons-lang3-3.4.jar:cp/commons-logging-1.1.1.jar:cp/cryptacular-1.0.jar:cp/cxf-core-3.0.12.jar:cp/cxf-rt-bindings-soap-3.0.12.jar:cp/cxf-rt-bindings-xml-3.0.12.jar:cp/cxf-rt-databinding-jaxb-3.0.12.jar:cp/cxf-rt-frontend-simple-3.0.12.jar:cp/cxf-rt-ws-addr-3.0.12.jar:cp/cxf-rt-ws-policy-3.0.12.jar:cp/cxf-rt-wsdl-3.0.12.jar:cp/dom4j-1.6.1.jar:cp/ehcache-2.10.3.jar:cp/el-api-2.2.jar:cp/geronimo-javamail_1.4_spec-1.7.1.jar:cp/guava-16.0.1.jar:cp/h2-1.0.60.jar:cp/hibernate-commons-annotations-5.0.1.Final.jar:cp/hibernate-core-5.2.2.Final.jar:cp/hibernate-entitymanager-5.2.2.Final.jar:cp/hibernate-jpa-2.1-api-1.0.0.Final.jar:cp/hibernate-validator-5.2.4.Final.jar:cp/hsqldb-2.3.3.jar:cp/httpclient-4.5.3.jar:cp/httpcore-4.4.5.jar:cp/httpmime-4.5.3.jar:cp/jackson-annotations-2.8.5.jar:cp/jackson-core-2.8.5.jar:cp/jackson-databind-2.8.5.jar:cp/jain-sip-ri-1.2.324.jar:cp/jandex-2.0.0.Final.jar:cp/jasypt-1.9.2.jar:cp/java-support-7.1.1.jar:cp/javassist-3.20.0-GA.jar:cp/javax.inject-1.jar:cp/javax.transaction-api-1.2.jar:cp/jaxb-core-2.2.11.jar:cp/jaxb-impl-2.2.11.jar:cp/jboss-interceptors-api_1.1_spec-1.0.0.Beta1.jar:cp/jboss-logging-3.3.0.Final.jar:cp/jcl-over-slf4j-1.7.25.jar:cp/jnotify-0.94.jar:cp/joda-time-2.9.6.jar:cp/jsr250-api-1.0.jar:cp/jul-to-slf4j-1.7.25.jar:cp/log4j-over-slf4j-1.7.25.jar:cp/logback-classic-1.1.8.jar:cp/logback-core-1.1.8.jar:cp/lombok-1.16.6.jar:cp/neethi-3.0.3.jar:cp/opensaml-2.5.1-1.jar:cp/opensaml-core-3.1.1.jar:cp/opensaml-profile-api-3.1.1.jar:cp/opensaml-saml-api-3.1.1.jar:cp/opensaml-saml-impl-3.1.1.jar:cp/opensaml-security-api-3.1.1.jar:cp/opensaml-security-impl-3.1.1.jar:cp/opensaml-soap-api-3.1.1.jar:cp/opensaml-xacml-api-3.1.1.jar:cp/opensaml-xacml-impl-3.1.1.jar:cp/opensaml-xacml-saml-api-3.1.1.jar:cp/opensaml-xacml-saml-impl-3.1.1.jar:cp/opensaml-xmlsec-api-3.1.1.jar:cp/opensaml-xmlsec-impl-3.1.1.jar:cp/openws-1.4.2-1.jar:cp/postgresql-9.4.1212.jre7.jar:cp/router-schema-0.0.1-SNAPSHOT.jar:cp/router-sip-0.0.1-SNAPSHOT.jar:cp/slf4j-api-1.7.25.jar:cp/snakeyaml-1.17.jar:cp/spring-aop-4.3.5.RELEASE.jar:cp/spring-aspects-4.3.5.RELEASE.jar:cp/spring-beans-4.3.5.RELEASE.jar:cp/spring-boot-1.4.3.RELEASE.jar:cp/spring-boot-autoconfigure-1.4.3.RELEASE.jar:cp/spring-boot-starter-1.4.3.RELEASE.jar:cp/spring-boot-starter-aop-1.4.3.RELEASE.jar:cp/spring-boot-starter-data-jpa-1.4.3.RELEASE.jar:cp/spring-boot-starter-jdbc-1.4.3.RELEASE.jar:cp/spring-boot-starter-logging-1.4.3.RELEASE.jar:cp/spring-boot-starter-security-1.4.3.RELEASE.jar:cp/spring-boot-starter-tomcat-1.4.3.RELEASE.jar:cp/spring-boot-starter-web-1.4.3.RELEASE.jar:cp/spring-context-4.3.5.RELEASE.jar:cp/spring-core-4.3.5.RELEASE.jar:cp/spring-data-commons-1.12.6.RELEASE.jar:cp/spring-data-jpa-1.10.6.RELEASE.jar:cp/spring-expression-4.3.5.RELEASE.jar:cp/spring-jdbc-4.3.5.RELEASE.jar:cp/spring-orm-4.3.5.RELEASE.jar:cp/spring-oxm-4.3.5.RELEASE.jar:cp/spring-security-config-4.1.4.RELEASE.jar:cp/spring-security-core-4.1.4.RELEASE.jar:cp/spring-security-web-4.1.4.RELEASE.jar:cp/spring-tx-4.3.5.RELEASE.jar:cp/spring-web-4.3.5.RELEASE.jar:cp/spring-webmvc-4.3.5.RELEASE.jar:cp/spring-ws-core-2.3.1.RELEASE.jar:cp/spring-ws-security-2.3.1.RELEASE.jar:cp/spring-xml-2.3.1.RELEASE.jar:cp/stax2-api-3.1.4.jar:cp/tomcat-embed-core-8.5.6.jar:cp/tomcat-embed-el-8.5.6.jar:cp/tomcat-embed-websocket-8.5.6.jar:cp/tomcat-jdbc-8.5.6.jar:cp/tomcat-juli-8.5.6.jar:cp/validation-api-1.1.0.Final.jar:cp/woodstox-core-asl-4.4.1.jar:cp/wsdl4j-1.6.3.jar:cp/wss4j-1.6.19.jar:cp/wss4j-ws-security-common-2.1.4.jar:cp/wss4j-ws-security-dom-2.1.4.jar:cp/xml-resolver-1.2.jar:cp/xmlschema-core-2.2.1.jar:cp/xmlsec-1.5.8.jar:cp/xmltooling-1.3.2-1.jar";
    options[1].optionString = "-Xmx12000m";
    args.options = options;
    args.ignoreUnrecognized = JNI_FALSE;

    JNI_CreateJavaVM(&jvm, (void **)&env, &args);
    return env;
}

void invoke_main() 
{
    jmethodID mainMethod;
    jclass javaClass;
    jobjectArray applicationArgs;

    javaClass = env->FindClass("org/springframework/boot/loader/JarLauncher");

    mainMethod = env->GetStaticMethodID(javaClass, "main", "([Ljava/lang/String;)V");

    applicationArgs = env->NewObjectArray(0, env->FindClass("java/lang/String"), NULL);

    env->CallStaticVoidMethod(javaClass, mainMethod, applicationArgs);
}

void init_jni_methods()
{
    javaClass = env->FindClass("com/some/direct/DirectEndpoint");
    javaMethodCreate = env->GetStaticMethodID(javaClass, "createModel", "([BIII)[B");
}

int invoke_create_model(std::shared_ptr<uint8_t> data, int length, std::vector<uint8_t> &templ)
{
    jbyteArray baArg = env->NewByteArray(length);
    env->SetByteArrayRegion(baArg, 0, length, data.get());
    jbyteArray jValue = NULL;
    jValue = (jbyteArray)env->CallStaticObjectMethod(javaClass, javaMethodCreate, baArg, rate, channels, bits);
    env->DeleteLocalRef(baArg);
    if (env->ExceptionOccurred()) // check if an exception occurred
    {
        if(jValue != NULL)
        {
           env->DeleteLocalRef(jValue);
        }
        return -1;
    }

    uint8_t* value = NULL;
    int len = 0;
    if(jValue != NULL)
    {
        len = env->GetArrayLength(jValue);
        value = env->GetByteArrayElements(jValue, JNI_FALSE);
        templ.assign(value, value + len);
        env->ReleaseByteArrayElements(jValue, value, JNI_ABORT);
        env->DeleteLocalRef(jValue);
        return 0;
    }
    else
    {
        printf("No enough data for creation");
        return -1;
    }
}

ReturnStatus createModel(const Record &record, std::vector<uint8_t> &templ)
{
    ReturnStatus ret = ReturnStatus(ReturnCode::Success, std::string("OK"));
    try
    {
        int i = invoke_create_model(record.data, record.length, templ);
        if( i != 0 )
        {
            ret = ReturnStatus(ReturnCode::CreationError, "Error during creation: no enough data");
        }
    }
    catch(...)
    {
        ret = ReturnStatus(ReturnCode::CreationError, "Error during creation: no enough data");
    }
    return ret;
}

ReturnStatus initialize(const std::string &configDir)
{
    if( init ==0 )
    {
        env = create_vm();
        invoke_main();
        init_jni_methods();
        init = 1;
    }
    return ReturnStatus(ReturnCode::Success, "OK");
}

首先调用initialize然后调用createModel。这两个调用都来自外部项目,其中记录和templ参数ara管理。

Java中没有内存泄漏,因为Java代码在生产中有数千个调用。使用此包装器的项目在使用:

调用createModel的一千零半迭代后崩溃
terminate called after throwing an instance of 'std::bad_alloc'
what():  std::bad_alloc
The program has unexpectedly finished.
/home/test/app crashed.

我已经看到了很多类似的问题,并在这个包装中检查了eveything,但它没有产生任何效果。

我忘了释放一些记忆吗?

我是否理解在Java代码中创建的所有内容以及未返回到C ++包装器的内容是否由Java GC管理?

UPD: 解决了。 问题是“templ”向量的数据存储在外部项目的RAM中。而外部项目的堆只限于1.3-2Gb。因此,经过大约1400次迭代后,每次出现此错误时都会推出1Mb数据。

0 个答案:

没有答案