内存分配究竟是如何进行的,Java 和 C 如何交互以跟踪同一对象?
How exactly is memory allocation taking place and how are Java and C interacting to keep track of the same object?
我有一个 class 包含一个 long 类型的成员变量和一组本机方法声明。我相信变量的内存是在一个本机方法中分配的,并且通过调用另一个本机方法 destroy 在 finalize() 方法中尝试取消分配。我知道 finalize 已被弃用,但在找到 finalize 的替代方案之前,我需要了解内存分配是如何发生的,Java 和 C 如何通过 JNI 保持同步,以及如何跟踪这个特定的成员变量。我会尝试用代码片段更好地解释这个场景:
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;
}
我困惑的地方:
为什么是 long 和 jlong 以及从用户定义类型 sq_stsm_t 到 long 的转换。我知道 Java 不知道这个用户定义的类型。但是为什么选择long呢?
为什么 SQ 上的 #define 以及 destroy 究竟如何通过调用 SQ 上的 delete 来删除在 set_shdl 中分配的内存?
Post 这个,我必须找到 finalize 的替代品 - 为此我得出结论,AutoCloseable with try-with-resources 是迄今为止我最好的选择。由于涉及很多本机调用,我对如何处理它感到困惑。但是,这是一个需要解决的单独问题,我不想在这里讨论。只是提到设置一些关于用例的背景。
一个Javalong
变量保证是64位宽的,所以它足以包含一个地址,即使在64位系统上也是如此。
强制转换为 long
只是为了让 C++ 类型系统满意。
如您所见,SQ
宏用于将此地址转换回指向 sq_stsm_t
对象的正确指针。
关于你的第二个问题:宏所做的只是摆脱一些打字工作。
程序的 Java 部分将始终传递 long
类型的 shdl
变量,因此 SQ
宏只提供对实际 sq_stsm_t
对象的轻松访问。
最后,C++中的new
和delete
走到了一起:new
分配内存并调用构造函数,delete
调用析构函数并再次释放内存。
请注意,"free" 并不一定意味着 "give back to the OS",因此您的进程的内存使用在这个 delete
操作后可以保持不变。
至于您代码中的注释:Java_com_project_package_JSQ_set_shdl
尝试调用 Java 方法 boolean com.project.package.JSQ#set_JSQ_shdl(jlong)
,尽管它不存在于您粘贴的代码中。我想这是 shdl
字段的简单 setter?
关于您的后续计划:实施 AutoCloseable
是个好主意。您需要在 JSQ 对象中创建一个 native void close()
方法,该方法调用存储在 shdl
字段中的 sq_stsm_t
对象的 delete
,然后替换 shdl
字段(jlong)nullptr
.
我有一个 class 包含一个 long 类型的成员变量和一组本机方法声明。我相信变量的内存是在一个本机方法中分配的,并且通过调用另一个本机方法 destroy 在 finalize() 方法中尝试取消分配。我知道 finalize 已被弃用,但在找到 finalize 的替代方案之前,我需要了解内存分配是如何发生的,Java 和 C 如何通过 JNI 保持同步,以及如何跟踪这个特定的成员变量。我会尝试用代码片段更好地解释这个场景:
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;
}
我困惑的地方:
为什么是 long 和 jlong 以及从用户定义类型 sq_stsm_t 到 long 的转换。我知道 Java 不知道这个用户定义的类型。但是为什么选择long呢?
为什么 SQ 上的 #define 以及 destroy 究竟如何通过调用 SQ 上的 delete 来删除在 set_shdl 中分配的内存?
Post 这个,我必须找到 finalize 的替代品 - 为此我得出结论,AutoCloseable with try-with-resources 是迄今为止我最好的选择。由于涉及很多本机调用,我对如何处理它感到困惑。但是,这是一个需要解决的单独问题,我不想在这里讨论。只是提到设置一些关于用例的背景。
一个Javalong
变量保证是64位宽的,所以它足以包含一个地址,即使在64位系统上也是如此。
强制转换为 long
只是为了让 C++ 类型系统满意。
如您所见,SQ
宏用于将此地址转换回指向 sq_stsm_t
对象的正确指针。
关于你的第二个问题:宏所做的只是摆脱一些打字工作。
程序的 Java 部分将始终传递 long
类型的 shdl
变量,因此 SQ
宏只提供对实际 sq_stsm_t
对象的轻松访问。
最后,C++中的new
和delete
走到了一起:new
分配内存并调用构造函数,delete
调用析构函数并再次释放内存。
请注意,"free" 并不一定意味着 "give back to the OS",因此您的进程的内存使用在这个 delete
操作后可以保持不变。
至于您代码中的注释:Java_com_project_package_JSQ_set_shdl
尝试调用 Java 方法 boolean com.project.package.JSQ#set_JSQ_shdl(jlong)
,尽管它不存在于您粘贴的代码中。我想这是 shdl
字段的简单 setter?
关于您的后续计划:实施 AutoCloseable
是个好主意。您需要在 JSQ 对象中创建一个 native void close()
方法,该方法调用存储在 shdl
字段中的 sq_stsm_t
对象的 delete
,然后替换 shdl
字段(jlong)nullptr
.