JNI problem

E

Edsoncv

Hello
I'm a newbie Java programmer and I'm facing problems with a JNI
interface that calls Java from a C++ code. The code simply calls Java
to evaluate a matemathical function. The code runs until the line 40 ("
xj = env->NewDoubleArray(4);" ) and exits with the exception "Out of
memory", which means that it couldn't allocate the DoubleArray. I hope
someone could help me. The code is below :

C++ code:

Bool eval_f(Index n, Number* x, Bool new_x,
Number* obj_value, UserDataPtr user_data)
{
assert(n == 4);
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
JavaVMOption options[5];
jint res;
jclass cls;
jmethodID mid;
jdoubleArray xj;
double res_obj;
options[0].optionString = "-Xms4M";
options[1].optionString = "-Xmx64M";
options[2].optionString = "-Xss512K";
options[3].optionString = "-Xoss400K";
options[4].optionString = "-Djava.class.path=.";
vm_args.version = JNI_VERSION_1_4;
vm_args.options = options;
vm_args.nOptions = 5;
vm_args.ignoreUnrecognized = JNI_FALSE;

/* Create the Java VM */
res = JNI_CreateJavaVM(&jvm,(void**)&env,&vm_args);
if (res < 0) {
cerr<< "Can't create Java VM" << endl;
goto destroy;
}
cls = env->FindClass("res_fobj");
if (cls == 0) {
cerr << "Can't find res_fobj class" << endl;
goto destroy;
}
mid = env->GetStaticMethodID(cls, "main", "([D)D");
if (mid == 0) {
cerr<< "Can't find res_fobj.main" << endl;
goto destroy;
}
xj = env->NewDoubleArray(4);
if (xj == 0) {
cerr << "Out of memory" << endl;
goto destroy;
}
res_obj = env->CallStaticDoubleMethod(cls, mid, xj);
if (res_obj == 0) {
cerr << "Out of memory" << endl;
goto destroy;
}
cout << "resobj Calculado por java:\t" << res_obj << endl;
*obj_value = res_obj;
cout << "obj_value atribuído: \t" << *obj_value << endl;

return TRUE;
destroy:
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
}
jvm->DestroyJavaVM();
return FALSE;

}


Java code:

public class res_fobj {
public static double main(double x[]){
double res;
res = x[0] * x[3] * (x[0] + x[1] + x[2]) + x[2];
return res;
}
}

Thanks
 
E

Edsoncv

Sorry the problem is in line 45 and not in line 40 as I mentioned. The
exception "Out of memory" happened after the call to "res_obj =
env->CallStaticDoubleMethod(cls, mid, xj);"
Thanks
 
L

Lee

Hi,

A much more learned collegue tells me that you cannot define the Java
main method like that - it must be public static void main(String
args[]). However, you can define another method that you call to do
your calculation, eg.


public class res_fobj
{
public static void main(String args[])
{
}

public static double doCalc(double x[])
{
double res;
res = x[0] * x[3] * (x[0] + x[1] + x[2]) + x[2];
return res;
}
}

Hope this helps!

Lee
Sorry the problem is in line 45 and not in line 40 as I mentioned. The
exception "Out of memory" happened after the call to "res_obj =
env->CallStaticDoubleMethod(cls, mid, xj);"
Thanks

Hello
I'm a newbie Java programmer and I'm facing problems with a JNI
interface that calls Java from a C++ code. The code simply calls Java
to evaluate a matemathical function. The code runs until the line 40 ("
xj = env->NewDoubleArray(4);" ) and exits with the exception "Out of
memory", which means that it couldn't allocate the DoubleArray. I hope
someone could help me. The code is below :

C++ code:

Bool eval_f(Index n, Number* x, Bool new_x,
Number* obj_value, UserDataPtr user_data)
{
assert(n == 4);
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
JavaVMOption options[5];
jint res;
jclass cls;
jmethodID mid;
jdoubleArray xj;
double res_obj;
options[0].optionString = "-Xms4M";
options[1].optionString = "-Xmx64M";
options[2].optionString = "-Xss512K";
options[3].optionString = "-Xoss400K";
options[4].optionString = "-Djava.class.path=.";
vm_args.version = JNI_VERSION_1_4;
vm_args.options = options;
vm_args.nOptions = 5;
vm_args.ignoreUnrecognized = JNI_FALSE;

/* Create the Java VM */
res = JNI_CreateJavaVM(&jvm,(void**)&env,&vm_args);
if (res < 0) {
cerr<< "Can't create Java VM" << endl;
goto destroy;
}
cls = env->FindClass("res_fobj");
if (cls == 0) {
cerr << "Can't find res_fobj class" << endl;
goto destroy;
}
mid = env->GetStaticMethodID(cls, "main", "([D)D");
if (mid == 0) {
cerr<< "Can't find res_fobj.main" << endl;
goto destroy;
}
xj = env->NewDoubleArray(4);
if (xj == 0) {
cerr << "Out of memory" << endl;
goto destroy;
}
res_obj = env->CallStaticDoubleMethod(cls, mid, xj);
if (res_obj == 0) {
cerr << "Out of memory" << endl;
goto destroy;
}
cout << "resobj Calculado por java:\t" << res_obj << endl;
*obj_value = res_obj;
cout << "obj_value atribuído: \t" << *obj_value << endl;

return TRUE;
destroy:
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
}
jvm->DestroyJavaVM();
return FALSE;

}


Java code:

public class res_fobj {
public static double main(double x[]){
double res;
res = x[0] * x[3] * (x[0] + x[1] + x[2]) + x[2];
return res;
}
}

Thanks
 
G

Gordon Beaton

A much more learned collegue tells me that you cannot define the
Java main method like that - it must be public static void
main(String args[]).

That's true only when you use the Java launcher. The OP is free to
invoke any methods he likes from his native code.

/gordon
 
G

Gordon Beaton

res_obj = env->CallStaticDoubleMethod(cls, mid, xj);
if (res_obj == 0) {
cerr << "Out of memory" << endl;
goto destroy;
}

The array you pass to the method contains 4 null references to Double.
That may or may not be ok, depending on what the method is expecting.
More likely you intended to initialize the array contents but forgot,
and as a result a NullPointerException is occurring in the method.

To get more information, use something like DescribeException()
instead of simply displaying "Out of memory" or other string constant
when a JNI function fails.

Note too that CallStaticDoubleMethod() returns a jdouble, not a jint.

/gordon
 
L

Lee

Gordon said:
A much more learned collegue tells me that you cannot define the
Java main method like that - it must be public static void
main(String args[]).

That's true only when you use the Java launcher. The OP is free to
invoke any methods he likes from his native code.

/gordon

Indeed - however, as this function was called main, I am assuming that
the OP didn't hava a 'proper' main function to use as an entry point?
Surely if the code is exactly as presented, then that would account for
the null value returned?

Lee
 
L

Lee

ps: I agree with you on the doubles issue though.
Gordon said:
A much more learned collegue tells me that you cannot define the
Java main method like that - it must be public static void
main(String args[]).

That's true only when you use the Java launcher. The OP is free to
invoke any methods he likes from his native code.

/gordon

Indeed - however, as this function was called main, I am assuming that
the OP didn't hava a 'proper' main function to use as an entry point?
Surely if the code is exactly as presented, then that would account for
the null value returned?

Lee
 
E

Edsoncv

Exactly as you describe I forgot to initiallize the array. Now a
have another problem. My programm needs to call the java class
"res_fobj" many times to evaluate a mathematical function. The first
time it returns the right result with no problem. But in the second
call, when I try to create again the JVM object I got an error and the
programm exits. The ExceptionDescribe(); gives a very large textfile
which I think it is not a good idea to paste here. I'll paste my
reformulated code. Thanks.

/* Function Implementations */
Bool eval_f(Index n, Number* x, Bool new_x,
Number* obj_value, UserDataPtr user_data)
{
assert(n == 4);


JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
JavaVMOption options[5];
jint res;
jclass cls;
jmethodID mid;
// jdoubleArray xj;
// jdouble res_obj;
jdoubleArray xj;
double res_obj, *jxpt;
// jobjectArray args;


options[0].optionString = "-Xms4M";
options[1].optionString = "-Xmx64M";
options[2].optionString = "-Xss512K";
options[3].optionString = "-Xoss400K";
options[4].optionString = "-Djava.class.path=.";

vm_args.version = JNI_VERSION_1_4;
vm_args.options = options;
vm_args.nOptions = 5;
vm_args.ignoreUnrecognized = JNI_FALSE;


for (int i = 0; i < n; i++){
// jxpt=x;
cout << "x(" << i << "):" << x << endl;
}

/* Create the Java VM */


res = JNI_CreateJavaVM(&jvm,(void**)&env,&vm_args);
if (res < 0) {
env->ExceptionDescribe();
cerr<< "Can't create Java VM" << endl;
destroyJVM(jvm, env);
}
cls = env->FindClass("res_fobj");
if (cls == 0) {
cerr << "Can't find res_fobj class" << endl;
destroyJVM(jvm, env);
}


mid = env->GetStaticMethodID(cls, "main", "([D)D");
if (mid == 0) {
cerr<< "Can't find res_fobj.main" << endl;
destroyJVM(jvm, env);
}
xj = env->NewDoubleArray(4);
if (xj == 0) {
cerr << "Out of memory" << endl;
destroyJVM(jvm, env);
}
jxpt = env->GetDoubleArrayElements(xj,0);
jxpt=x;
for (int i = 0; i < n; i++){
cout << "jxpt(" << i << "):" << jxpt << endl;
}
env->ReleaseDoubleArrayElements(xj, jxpt, 0);
res_obj = env->CallStaticDoubleMethod(cls, mid, xj);
if (res_obj == 0) {
cerr << "Out of memory2" << endl;
destroyJVM(jvm, env);
}
cout << "resobj Calculado por java:\t" << res_obj << endl;
*obj_value = res_obj;
cout << "obj_value atribuído: \t" << *obj_value << endl;
destroyJVM(jvm, env);
return TRUE;

}


void destroyJVM(JavaVM *jvm, JNIEnv *env){
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
}
jvm->DestroyJavaVM();
}
 
G

Gordon Beaton

Exactly as you describe I forgot to initiallize the array. Now a
have another problem. My programm needs to call the java class
"res_fobj" many times to evaluate a mathematical function. The first
time it returns the right result with no problem. But in the second
call, when I try to create again the JVM object I got an error and the
programm exits.

In your code, you invoke destroyJVM() after each intermediate failure,
and again at the end of eval_f() before returning. If you do get an
intermediate failure, the second call to DestroyJavaVM() will probably
itself fail.

However I beleive that DestroyJavaVM() does not work, at any rate not
well enough for you to be able to start a new JVM in the same process.

So just create one JVM and reuse it for all of your method calls.

Note too that you need to use DeleteLocalRef() to explicitly free the
double[] you create each time you invoke eval_f. The garbage collector
cannot determine when you are finished using it, since you never
"return" to Java from this code.

/gordon
 
G

Gordon Beaton

Indeed - however, as this function was called main, I am assuming
that the OP didn't hava a 'proper' main function to use as an entry
point? Surely if the code is exactly as presented, then that would
account for the null value returned?

static void main(String[] args) is only special to the Java launcher,
not to the JVM itself. Since he has written his own launcher, the only
method that gets invoked is the one he invokes explicitly. The
existence (or lack) of any other main() is irrelevant.

/gordon
 
E

Edsoncv

Gordon said:
In your code, you invoke destroyJVM() after each intermediate failure,
and again at the end of eval_f() before returning. If you do get an
intermediate failure, the second call to DestroyJavaVM() will probably
itself fail.

However I beleive that DestroyJavaVM() does not work, at any rate not
well enough for you to be able to start a new JVM in the same process.

So just create one JVM and reuse it for all of your method calls.

I have a question about this: If I create a JVM at may main C++
programm, pass its pointer and env pointer to the eval_f function, do I
need to start a new thread (with AttachCurrentThread ) each time I call
eval_f or res_fobj?

Note too that you need to use DeleteLocalRef() to explicitly free the
double[] you create each time you invoke eval_f. The garbage collector
cannot determine when you are finished using it, since you never
"return" to Java from this code.

You mean "env->DeleteLocalRef(xj);"?

Is it necessary to "env->DeleteLocalRef(cls);"?

Thanks Again
 
G

Gordon Beaton

I have a question about this: If I create a JVM at may main C++
programm, pass its pointer and env pointer to the eval_f function, do I
need to start a new thread (with AttachCurrentThread ) each time I call
eval_f or res_fobj?

If you make all of your calls from the same original thread, then you
can reuse the original env pointer. However if you create any threads,
then you must attach them to get a valid env pointer for that thread.
You mean "env->DeleteLocalRef(xj);"?

Is it necessary to "env->DeleteLocalRef(cls);"?

Actually I meant xj, but now that you mention it, cls is also an
object reference that also needs to be freed.

One simple mechanism is to bracket the function contents with
PushLocalFrame() and PopLocalFrame(). That will take care of all of
the local references automatically, you don't need to delete any of
them explicitly.

/gordon
 
E

Edsoncv

One simple mechanism is to bracket the function contents with
PushLocalFrame() and PopLocalFrame(). That will take care of all of
the local references automatically, you don't need to delete any of
them explicitly.

I tried the use of "PushLocalFrame()" and "PopLocalFrame()". And I got
the same error as if I was using "env->DeleteLocalRef(xj);" and
"env->DeleteLocalRef(cls);". I read the documentation and didn't
understand completly what it does: These functions "tracks" all the
objects created related with the "env" variable and than delete them?

My program is like that: I start the jvm and env in my main program
pass a structure with both. In eval_f I cast back this structure and
calls the java function res_fobj.
The program called the eval_f (and also res_fobj) two times. The first
one it calculates without problems, but in the second it calculates and
them return with error. It happens also a strange thing: when I run the
program with valgring ( a programm to detect memory leaks) it runs
perfectely (with many errors!!!). The programm is below (just a part of
it):

struct J1 {
JNIEnv *env;
JavaVM *jvm;};

/* Function Implementations */
Bool eval_f(Index n, Number* x, Bool new_x,
Number* obj_value, UserDataPtr user_data)
{
assert(n == 4);
J1 *jj1p = (J1*)user_data;
JNIEnv *env = jj1p->env;
jint refe;
jclass cls;
jmethodID mid;
jdoubleArray xj;
double *jxpt;
refe = env->PushLocalFrame(20);
cls = env->FindClass("res_fobj");
mid = env->GetStaticMethodID(cls, "main", "([D)D");
xj = env->NewDoubleArray(4);
jxpt = env->GetDoubleArrayElements(xj,0);
jxpt=x;
env->ReleaseDoubleArrayElements(xj, jxpt, 0);
res_obj = env->CallStaticDoubleMethod(cls, mid, xj);
cout << "resobj Calculado por java:\t" << res_obj << endl;
*obj_value = res_obj;
env->PopLocalFrame(NULL);
return TRUE;

}

Edson CV
 
G

Gordon Beaton

I tried the use of "PushLocalFrame()" and "PopLocalFrame()". And I
got the same error as if I was using "env->DeleteLocalRef(xj);" and
"env->DeleteLocalRef(cls);". I read the documentation and didn't
understand completly what it does: These functions "tracks" all the
objects created related with the "env" variable and than delete
them?

Yes. I was not suggesting that Push/PopLocalFrame would cause any
different result, it just means you don't have to keep track of the
references yourself.
xj = env->NewDoubleArray(4);
jxpt = env->GetDoubleArrayElements(xj,0);
jxpt=x;
env->ReleaseDoubleArrayElements(xj, jxpt, 0);

Above you modify the *pointer* returned by GetDoubleArrayElements, not
the contents of the array it points to.

Realize that the pointer you pass to ReleaseDoubleArrayElements()
*must* come from GetDoubleArrayElements(), and by not doing so you
will have unpredictable results.

To initialize the contents of the array, consider transferring the
values indivudually from x to jxpt, or use SetDoubleArrayRegion()
instead.

/gordon
 
E

Edsoncv

xj = env->NewDoubleArray(4);
Above you modify the *pointer* returned by GetDoubleArrayElements, not
the contents of the array it points to.

Realize that the pointer you pass to ReleaseDoubleArrayElements()
*must* come from GetDoubleArrayElements(), and by not doing so you
will have unpredictable results.

To initialize the contents of the array, consider transferring the
values indivudually from x to jxpt, or use SetDoubleArrayRegion()
instead.

Mr. Beaton

You were absolutly right I rewrite the part of my program you
mentioned to:

xj = env->NewDoubleArray(4);
jxpt = env->GetDoubleArrayElements(xj,0);
for (int i = 0; i < n; i++){
jxpt=x;
cout << "jxpt(" << i << "):" << jxpt << endl;
}
env->ReleaseDoubleArrayElements(xj, jxpt, 0);

And it worked perfectly. I have serious problem with pointers!!! :)
Thanks a lot !!!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,066
Latest member
VytoKetoReviews

Latest Threads

Top