i’m making a dll in c++ that reads fields via jni. after a while whenever the game garbage collects it stutters.
the memory view in visual studio doesn’t show any change in memory usage so i doubt it’s a memory leak.
this is where i assume it’s happening but i cannot see why / how it happens.
how can i figure out what’s causing the garbage collector to take so long?
jobject get_class_loader() {
jclass thread_clazz = env->FindClass("java/lang/Thread");
jmethodID curthread_mid = env->GetStaticMethodID(thread_clazz, "currentThread", "()Ljava/lang/Thread;");
jobject thread = env->CallStaticObjectMethod(thread_clazz, curthread_mid);
jmethodID threadgroup_mid = env->GetMethodID(thread_clazz, "getThreadGroup", "()Ljava/lang/ThreadGroup;");
jclass threadgroup_clazz = env->FindClass("java/lang/ThreadGroup");
jobject threadgroup_obj = env->CallObjectMethod(thread, threadgroup_mid);
jmethodID groupactivecount_mid = env->GetMethodID(threadgroup_clazz, "activeCount", "()I");
jfieldID count_fid = env->GetFieldID(threadgroup_clazz, "nthreads", "I");
jint activeCount = env->GetIntField(threadgroup_obj, count_fid);
jobjectArray arrayD = env->NewObjectArray(activeCount, thread_clazz, NULL);
jmethodID enumerate_mid = env->GetMethodID(threadgroup_clazz, "enumerate", "([Ljava/lang/Thread;)I");
env->CallIntMethod(threadgroup_obj, enumerate_mid, arrayD);
jobject array_elements = env->GetObjectArrayElement(arrayD, 0);
jmethodID threadclassloader = env->GetMethodID(thread_clazz, "getContextClassLoader", "()Ljava/lang/ClassLoader;");
if (threadclassloader != NULL) {
jobject classloader_obj = env->CallObjectMethod(array_elements, threadclassloader);
env->DeleteLocalRef(array_elements);
env->DeleteLocalRef(thread_clazz);
env->DeleteLocalRef(thread);
env->DeleteLocalRef(threadgroup_clazz);
env->DeleteLocalRef(threadgroup_obj);
env->DeleteLocalRef(arrayD);
return classloader_obj;
}
else {
env->DeleteLocalRef(array_elements);
env->DeleteLocalRef(thread_clazz);
env->DeleteLocalRef(thread);
env->DeleteLocalRef(threadgroup_clazz);
env->DeleteLocalRef(threadgroup_obj);
env->DeleteLocalRef(arrayD);
return 0;
}
}
jclass get_object(const char* class_name) {
jclass clazz = env->FindClass("java/lang/Class");
jmethodID class_for_name_method = env->GetStaticMethodID(clazz, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
if (class_for_name_method == NULL) {
env->FatalError("Class.forName(String,boolean,java.lang.ClassLoader)java.lang.Class method not found\n");
}
jstring class_to_load = env->NewStringUTF(class_name);
auto class_loader = get_class_loader();
jclass jar_class = (jclass)env->CallStaticObjectMethod(clazz, class_for_name_method, class_to_load, true, class_loader);
env->ReleaseStringUTFChars(class_to_load, NULL);
env->DeleteLocalRef(class_to_load);
env->DeleteLocalRef(clazz);
env->DeleteLocalRef(class_loader);
return jar_class;
}
instance = std::make_unique<c_minecraft>();
auto minecraft_inst = instance->get_minecraft();
if (!minecraft_inst) {
env->DeleteLocalRef(minecraft_inst);
return;
} // do field suff after here. once done that delete local reference of minecraft_inst
Related
I have native method that returns Class<?>[] value. I trying to put jclass as jobject element to object array. But when i calling my native method from java, happens nothing (seriously, just nothing, method wont finish executing). There my code:
... other code where i makes some magic with classes
jobjectArray out = env->NewObjectArray(count /* count is classes count that i want to return */, caller /* method caller */, NULL);
for (int i = 0; i < count; ++i) {
jclass clazz = classes[i];
env->SetObjectArrayElement(out, i, clazz);
}
return out;
How to Pass and return Array of properties in jni (java to c++) as well as (c++ to java)
import java.util.*;
public class Test {
public native static Properties[] getStudentDetails();
public static void main(String[] args) {
System.loadLibrary("Sample");
int a= 10;
Properties[] records = getStudentDetails();
for(Properties record:records){
System.out.print("\ntype:"+record.getProperty("type"));
System.out.print("\ttime:"+record.getProperty("time"));
System.out.print("\tsource:"+record.getProperty("source"));
System.out.print("\teid:"+record.getProperty("eid"));
System.out.println("");
}
}
}
it give me error
Assuming you have access to a std::vector<SearchRecord*> searchRecordResult somewhere:
extern "C"
JNIEXPORT jobjectArray JNICALL Java_Test_getStudentDetails(JNIEnv *env, jclass cls) {
// Get Properties class, its constructor and the put method
jclass cls_Properties = env->FindClass("java/util/Properties");
jmethodID mid_Properties_ctor = env->GetMethodID(cls_Properties, "<init>", "()V");
jmethodID mid_Properties_put = env->GetMethodID(cls_Properties, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
// Construct the key Strings up front
jstring key_type = env->NewStringUTF("type");
jstring key_time = env->NewStringUTF("time");
jstring key_source = env->NewStringUTF("source");
jstring key_eid = env->NewStringUTF("eid");
jobjectArray ret = env->NewObjectArray(searchRecordResult.size(), cls_Properties, 0);
for (int i = 0; i < searchRecordResult.size(); i++) {
auto result = searchRecordResult[i];
// Allocate and fill a Properties object, making sure to clean up the value Strings.
env->PushLocalFrame(5);
jobject prop = env->NewObject(cls_Properties, mid_Properties_ctor);
env->CallObjectMethod(prop, mid_Properties_put, key_type, env->NewStringUTF(result->type));
env->CallObjectMethod(prop, mid_Properties_put, key_time, env->NewStringUTF(result->time));
env->CallObjectMethod(prop, mid_Properties_put, key_source, env->NewStringUTF(result->source));
env->CallObjectMethod(prop, mid_Properties_put, key_eid, env->NewStringUTF(result->eid));
prop = env->PopLocalFrame(prop);
env->SetObjectArrayElement(ret, i, prop);
}
return ret;
}
The core loop just constructs Properties objects, fills their properties with put and assigns them to the right slot on the ret array.
I'm badly stuck in one basic date conversion in jni as the (long) values passed from Java to JNI/C++ are quite different.
My Java date objects are initialized in class as,
Date date1=new Date(1220227200L * 1000);
// If I convert into milliseconds such as
long timeInMillisecond=date1.getTime();
System.out.println (timeInMillisecond )
// it obviously displays 1220227200000
In my native function, i'm retrieving as
jfieldID fid = env->GetFieldID(cls, "date1", "Ljava/util/Date");
// case 1 - value 1
jlong dobject = (jlong) env->GetObjectField(object ,fid);
cout <<dobject; //displays 139757766370904
// case 2 - value 2
long dobject2 = (long) env->GetObjectField(object, fid);
cout <<dobject2; // displays 140031771862616
// case 3 - value 3
long long dobject3 = (long long) env->GetObjectField(object ,fid);
cout <<dobject3; //displays 140456034100824
I wonder what would be the best way to get correct value here from Java in C++. I'm using C++ 11. Some one please help me here.
Update
Sorry guys, i think I misspelled a few variables, so it wasn't dobject2,3 in GetObjectfield (correcting that)and infact rewriting the code here to make it more clear.
//Data.java
class Data {
public long sDate;
public Date schedDate;
....
}
// Test.java
// Native function
public native static void sendEvent(Data EventRec);
Data Rec= new DataRec();
Rec.sDate= 1400754723399L;
Rec.schedDate = new Date(1220227200L * 1000);
sendEvent(Rec);
//C++
JNIEXPORT void JNICALL Java_Test_sendEvent (JNIEnv *env, jclass cls, jobject jobj) {
cls = env->FindClass("Data");
if (cls !=NULL) {
jmethodID ctorID = env->GetMethodID(cls, "<init>","()V");
if (ctorID != NULL) {
jfieldID fidLong = env->GetFieldID(cls, "sDate", "J");
long dObj = (long) env->GetLongField(jobj,fidLong);
cout << "C++ .. Event Date (LONG): " << dObj <<endl; // This is correct
jfieldID fidDate = env->GetFieldID(cls, "schedDate", "Ljava/util/Date");
// Here is the problem area, that I tried to mimic above...
jobject dobject= (env->GetObjectField(jobj,fidDate));
long dobj = env->GetLongField(dobject,fidDate);
// OR
// long dObj2 = (long) env->GetLongField(jobj,fidDate);
// .....
cout << "C++ .. Date (DATE)..." << dobj;
}
}
}
Your JNI code is not accessing the Java objects correctly.
In your Java_Test_sendEvent() implementation, the jclass parameter points to the class type that your public native static void sendEvent(Data EventRec); is declared in (which you did not show), and the jobject parameter points to the Data Rec object that your Java code is passing in to sendEvent().
Also, in your call to env->GetFieldID() for the Date field, you are missing a required semicolon at the end of the type signature string.
Try this instead:
JNIEXPORT void JNICALL Java_Test_sendEvent (JNIEnv *env, jclass cls, jobject EventRec)
{
jclass cls_EventRec = env->GetObjectClass(EventRec);
jfieldID fid_sDate = env->GetFieldID(cls_EventRec, "sDate", "J");
if (!fid_sDate) {
// error handling...
return;
}
jfieldID fid_schedDate = env->GetFieldID(cls_EventRec, "schedDate", "Ljava/util/Date;");
if (!fid_schedDate) {
// error handling...
return;
}
jlong sDate = env->GetLongField(EventRec, fid_sDate);
cout << "C++ .. Event Date (LONG): " << sDate << endl;
jobject schedDate = env->GetObjectField(EventRec, fid_schedDate);
if (schedDate) {
jclass cls_schedDate = env->GetObjectClass(schedDate);
jmethodID mid_getTime = env->GetMethodID(cls_schedDate, "getTime", "()J");
jlong timeInMillisecond = env->CallLongMethod(schedDate, mid_getTime);
cout << "C++ .. Date (DATE)..." << timeInMillisecond << endl;
}
}
You're overlooking something very important:
jfieldID fid = env->GetFieldID(cls, "date1", "Ljava/util/Date;");
jobject dobject = env->GetObjectField(dobject, fid); // Note: "Get Object Field"
This returns a java.util.Date object. You need to call getTime on it as well:
jmethodID mid = env->GetMethodID(cls, "getTime", "()J");
jlong timeInMillisecond = env->CallLongMethod(dobject, mid);
I am trying to call java method from C, but getting below error
Status after JNI_CreateJavaVM=<0>
restCall method found:: Avinash Kumar
Execution error : file ''
error code: 114, pc=0, call=1, seg=0
114 Attempt to access item beyond bounds of memory (Signal 11)
Target is to send String as an argument from C to Java function.
Below is the code :
#include <jni.h>
#define ENV (*env)
enum eConst
{
MAX_Options = 21 ,
MAX_LREF = 120 ,
PARSING_ERROR = -10 ,
ERROR = -999 ,
EXCEPTION_ERROR = -100 ,
OK = 1 ,
YES = 'Y' ,
NO = 'N'
};
int main()
{
jint jRet;
jstring jszRes;
static JavaVM *jvm = NULL;
static JNIEnv *env = NULL ;
JavaVMInitArgs vm_args;
JavaVMOption options[MAX_Options-1];
int optionCount=0;
options[optionCount++].optionString = "-verbose:jni,class,gc";
char *path="user/work/avikumar/";
char *class_path="user/work/avikumar/";
char path_option[2000]={'\n'};
sprintf(path_option,"-Djava.class.path=%s/MyClass.class:%s/jersey-core-1.19.jar:%s/jersey-client-1.19.jar:%s/javax.ws.rs-api-2.0-m02.jar:.",class_path,path,path,path);
options[optionCount++].optionString = path_option;
options[optionCount++].optionString = "-Xms128m";
options[optionCount++].optionString = "-Xmx512m";
options[optionCount++].optionString = "-Xss8m";
vm_args.options = options;
vm_args.nOptions = optionCount;
vm_args.version = JNI_VERSION_1_6;
vm_args.ignoreUnrecognized = JNI_FALSE;
jRet = JNI_CreateJavaVM(&jvm,(void**)&env,(void*)&vm_args);
printf("Status after JNI_CreateJavaVM=<%d>\n",jRet);
if (jRet < 0)
{
return(-4);
}
if( ENV->EnsureLocalCapacity(env, MAX_LREF) < 0)
{
printf( "\n out of memory. Program terminated\n");
(*jvm)->DestroyJavaVM(jvm);
return(-5);
}
jclass jlocClass;
jlocClass= ENV->FindClass(env, "MyClass");
if (jlocClass == NULL || (*env)->ExceptionOccurred(env))
{
printf("Can not load the main class\n");
return (-6);
}
jmethodID restCall_method;
restCall_method = ENV->GetMethodID(env, jlocClass,"restCall","(Ljava/lang/String;)V");
if( restCall_method == NULL)
{
printf("restCall method not found\n");
return (-7);
}
char *inp = "Avinash Kumar";
printf("restCall method found:: %s\n",inp);
//jstring jstr = (*env)->NewStringUTF(env,inp);
jstring jstr1=(*env)->NewStringUTF(env, "Avinash Kumar");
ENV->CallVoidMethod(env, jlocClass, restCall_method,jstr1);
(*jvm)->DestroyJavaVM(jvm);
return 0;
}
MyClass.java
class MyClass
{
public static void main(String args[])
{
System.out.println("Hello Avinash\n");
}
public void restCall(String inp)
{
System.out.println(inp+ " :: Hello Avinash from restCall\n");
}
}
You can't mix static/non-static context inside JNI. You have two choices.
Static method
restCall_method = ENV->GetStaticMethodID(
env, jlocClass, "restCall", "(Ljava/lang/String;)V");
...
...
...
ENV->CallStaticVoidMethod(env, jlocClass, restCall_method,jstr1);
and, inside Java
public static void restCall(String inp)
or non-static method
jmethodID init = ENV->GetMethodID(env,jlocClass, "<init>", "()V");
jobject obj_MyClass = ENV->NewObject(env,jlocClass, init);
jmethodID restCall_method;
restCall_method = ENV->GetMethodID(env, jlocClass,"restCall","(Ljava/lang/String;)V");
...
...
...
ENV->CallVoidMethod(env, obj_MyClass, restCall_method,jstr1);
and, inside Java
public void restCall(String inp)
In first case, you have to use Static family methods to get id and call target method, in second case, you have to create instance of class and use methods for object based context.
I'm having problems with my FindClass call which returns null:
JData::JIgZorroBridgeClass = env->FindClass(JData::IgZorroBridgePath);
Most answers I find about this points to missing package name, or class loader issues.
IgZorroBridgePath here is my class with fully qualified package name, ie. com/igz/Zorro.
Also as you can see I'm initializing the JVM right before this FindClass call, so I think I should be on the correct thread already.
Is there anything else I can check?
#include <assert.h>
#include "JNIHandler.hpp"
#include "JReferences.hpp"
const char* JVMClassPathOption = "-Djava.class.path=Plugin/ig/igplugin-0.1.jar";
const char* IgZorroBridgePath = "com/danlind/igz/ZorroBridge";
void JNIHandler::init()
{
initializeJVM();
initializeJavaReferences();
}
void JNIHandler::initializeJVM()
{
if(isJVMLoaded) return;
JavaVMInitArgs args;
JavaVMOption options[1];
args.version = JData::JNI_VERSION;
args.nOptions = 1;
args.options = options;
options[0].optionString = (char*)JData::JVMClassPathOption;
args.ignoreUnrecognized = JNI_FALSE;
jint res = JNI_CreateJavaVM(&jvm, (void **)&env, &args);
assert(res == JNI_OK);
isJVMLoaded = true;
}
JNIEnv* JNIHandler::getJNIEnvironment(){
return env;
}
JNIEnv* JNIHandler::getEnvForCurrentThread()
{
int envStat = jvm->GetEnv((void **)&env, JData::JNI_VERSION);
if (envStat == JNI_EDETACHED) {
jint res = jvm->AttachCurrentThread((void**)&env, NULL);
assert (res == JNI_OK);
}
return env;
}
void JNIHandler::initializeJavaReferences()
{
if(areJavaReferencesInitialized) return;
initExceptionHandling();
initBridgeObject();
registerNatives();
areJavaReferencesInitialized = true;
}
void JNIHandler::initBridgeObject()
{
JData::JIgZorroBridgeClass = env->FindClass(JData::IgZorroBridgePath);
checkJNIExcpetion(env);
registerClassMethods();
JData::JIgZorroBridgeObject = env->NewObject(JData::JIgZorroBridgeClass, JData::constructor.methodID);
checkJNIExcpetion(env);
}
void JNIHandler::initExceptionHandling()
{
JData::ExceptionClass = env->FindClass(JData::ExcPath);
JData::excGetName.methodID = env->GetMethodID(JData::ExceptionClass, JData::excGetName.name, JData::excGetName.signature);
}
void JNIHandler::registerNatives()
{
JData::JZorroClass = env->FindClass(JData::ZorroPath);
env->RegisterNatives(JData::JZorroClass, JData::nativesTable, JData::nativesTableSize);
checkJNIExcpetion(env);
}
void JNIHandler::registerClassMethods()
{
for(auto *desc : JData::igZorroBridgeMethods)
{
desc->methodID = env->GetMethodID(JData::JIgZorroBridgeClass, desc->name, desc->signature);
checkJNIExcpetion(env);
}
}
void JNIHandler::checkJNIExcpetion(JNIEnv* env)
{
jthrowable exc = env->ExceptionOccurred();
if (!exc) return;
jclass exccls(env->GetObjectClass(exc));
jstring name = static_cast<jstring>(env->CallObjectMethod(exccls, JData::excGetName.methodID));
char const* utfName(env->GetStringUTFChars(name, 0));
JData::excGetMessage.methodID = env->GetMethodID(exccls, JData::excGetMessage.name, JData::excGetMessage.signature);
jstring message = static_cast<jstring>(env->CallObjectMethod(exc, JData::excGetMessage.methodID));
char const* utfMessage(env->GetStringUTFChars(message, 0));
BrokerError(utfName);
BrokerError(utfMessage);
env->ReleaseStringUTFChars(message, utfMessage);
env->ReleaseStringUTFChars(name, utfName);
env->ExceptionClear();
}
I was using the spring boot maven plugin, which reshuffles the package structure. I switched to using the shade plugin instead, which doesn't move packages around. This enabled FindClass to find my java class.