How exactly is memory allocation taking place and how are Java and C interacting to keep track of the same object?

userAsh :

I have a class containing a member variable of type long and a set of native method declarations. I believe the memory for the variable is being allocated in one of the native methods and the de-allocation is attempted in the finalize() method by calling another native method destroy. I understand that finalize is deprecated but prior to finding an alternative for finalize, I need to understand how the memory allocation is happening and how Java and C maintain sync through JNI and how this particular member variable is tracked. I will attempt to explain the scenario better with code snippets:

JSQ.java

class JSQ {
    protected long shdl;
    protected JSQ() { log("JSQ constructor"); }
    protected JSQ(String stmt, boolean isd)
        throws DhSQLException
    {
        // calls a set of native methods
        set_shdl();
        setstmt(stmt, shdl);
        setd(isd, shdl);
        prep(shdl);
    }

    public void finalize()
    {
        destroy(shdl);
    }

    private native void set_shdl() throws DhSQLException;
    private native void setstmt(String s, long shdl) throws DhSQLException;
    private native void setd(boolean b, long shdl);
    private native void prep(long shdl) throws DhSQLException;
    private native void destroy(long shdl);

    protected void execute() throws DhSQLException
    {
        parExec(shdl);
    }

    protected native void parExec(long shdl);
}

JSQL.cxx

#define SQ ((sq_stsm_t *)(shdl))

JNIEXPORT void JNICALL Java_com_project_package_JSQ_set_shdl
(JNIEnv *env, jobject obj_This)
{
    jclass cls;
    jmethodID mid;
    cls = (env)->GetObjectClass (obj_This);
    mid = (env)->GetMethodID (hThis,"set_JSQ_shdl","(J)V");

    status_t status;
    // memory allocation
    sq_stsm_t * S = new sq_stsm_t(status);
    if(status)
    {
        if (S) { delete S; }
        return;
    }
    // I understand that we're attempting to call a Java method from a native method.
    // But which method is it calling?
    // Also, are we converting S from sq_stms_t type to jlong?
    (env)->CallVoidMethod (obj_This,mid,(jlong) S);
    return;
}

JNIEXPORT void JNICALL Java_com_project_package_JSQ_setstmt
(JNIEnv *env, jobject, jstring jstmt, jlong shdl)
{
    status_t status;

    // cstmt is obtained using jstmt

    // Note: #define SQ ((sq_stsm_t *)(shdl))
    status = SQ->SetStmt(cstmt);

    return;
}

JNIEXPORT void JNICALL Java_com_project_package_JSQ_destroy
(JNIEnv *, jobject, jlong shdl)
{
    delete SQ;
}

The points where I'm confused:

  1. Why long and jlong and the conversion from the user defined type sq_stsm_t to long. I understand that Java is not aware of this user defined type. But why the choice of long?

  2. Why the #define on SQ and how exactly does destroy delete the memory allocated in set_shdl by calling delete on SQ?

Post this, I have to find a replacement for finalize - for which I have concluded that AutoCloseable with try-with-resources is, by far, my best option. Since there are a lot of native calls involved, I'm confused as to how I would go about it. But, that is a separate question to be addressed and I don't want to go into that here. Just mentioning to set some background on the use case.

Botje :

A Java long variable is guaranteed to be 64 bits wide, so it is enough to contain an address, even on a 64-bit system. The cast to long is just there to make the C++ type system happy. As you see, the SQ macro is used to convert this address back to a proper pointer to an sq_stsm_t object.

As for your second question: all the macro does is get rid of some typing work. The Java part of your program will always pass the long-typed shdl variable, so the SQ macro just provides easy access to the actual sq_stsm_t object.

Finally, new and delete in C++ go together: new allocates memory and calls the constructor, delete calls the destructor and frees the memory again. Note that "free" does not necessarily mean "give back to the OS", so your process's memory usage can remain the same after this delete operation.

As for the comment in your code: Java_com_project_package_JSQ_set_shdl tries to call the Java method boolean com.project.package.JSQ#set_JSQ_shdl(jlong), although it is not present in the code you pasted. I guess it is a simple setter for the shdl field?

As for your follow-up plans: implementing AutoCloseable is a good idea. You would need to create a native void close() method in your JSQ object which calls delete on the sq_stsm_t object stored in the shdl field and then replaces the shdl field with (jlong)nullptr.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326955&siteId=1