I'm using the JNI Invocation API in an attempt to wrap a Java library in C++.
My C++ classes are essentially containers for JNI jobjects with setter/getter methods to call JNI and retrieve data from said jobjects. What I am trying to figure out is how exactly I should go about trying to copy my class instances. For example, I want to do this:
absolute_date CURRENT_DATE = DATE;
My solution to this at the moment is to have a copy constructor which works like so:
Make JNI calls to retrieve the member data of DATE.
Make JNI calls to create a new jobject with the retrieved data.
Set the jobject in CURRENT_DATE equal to the newly created jobject
This method is of course somewhat arduous, both programmatically and in terms of efficiency.
Copying over the jobject would be ideal however it means that if DATE goes out of scope then the absolute_date constructor will be called and in turn calling DeleteLocalRef (thus making the jobject for the newly created object invalid).
Is there a way to do what I want? I.e. is it possible to create unique clones of java objects within the JVM via JNI so that I can have 2 or more unique jobject references that each refer to unique java objects that have the same state (i.e. same member values etc)? I should mention that I cannot touch the Java source code. This all has to be done via calls to the invocation API.
If not, is there a way to do this using multiple jobject references i.e. if I have ten copies of DATE, each sharing the same jobject, is it somehow possible to get the destructor to ONLY call when the last remaining DATE Object gets destroyed? Smart pointers perhaps? If so how would I go about doing this as I am relatively new to this.
Thanks,
Related
Suppose I'm using JNI to call some Java method that returns String, i. e. jstring in native code:
jstring jStr = (jstring)env->CallStaticObjectMethod(myApplicationClass, getString);
Do I then need to call env->DeleteLocalRef(jStr)? I think I do, but I can't find any specific instructions that state so in the JNI reference, and I can see I have a lot of code that doesn't call it - tried and true code. But if there was a minor memory leak, no one would notice since this code doesn't create many objects.
Every local reference you create is automatically freed when your JNI-called function returns to Java.
The JNI specification outlines two cases where you might want to use DeleteLocalRef:
A native method accesses a large Java object, thereby creating a local reference to the Java object. The native method then performs
additional computation before returning to the caller. The local
reference to the large Java object will prevent the object from being
garbage collected, even if the object is no longer used in the
remainder of the computation.
In other words, if you allocated a multi-megabyte string and no longer need it, you can delete the reference immediately, instead of leaving it to the JVM when you return to it. However, this is only useful if you need to perform additional steps in the JNI world before returning.
Note that this situations talks about a JNI function that is called from Java.
By attaching native threads to the JVM you can end up in the reverse situation, where your native code calls into the JVM. In that situation the JVM will not auto-free your local references and you need to delete local references yourself.
As a concrete example of that case, all the local references created in this function in the JNI cookbook will linger; they are never cleaned up manually.
A native method creates a large number of local references, although not all of them are used at the same time. Since the VM needs
a certain amount of space to keep track of a local reference, creating
too many local references may cause the system to run out of memory.
For example, a native method loops through a large array of objects,
retrieves the elements as local references, and operates on one
element at each iteration. After each iteration, the programmer no
longer needs the local reference to the array element.
This one is simpler: there is an upper limit to the number of local references.
Yes you must dispose the local references returned by Java callbacks. The standard rules apply: instead of DeleteLocalRef(), you can use PushLocalFrame()/PopLocalFrame(), or the local reference will be released automatically when the thread is detached from JVM, or the native method returns to Java (if this happens inside a native method).
is this right way of deleteing reference for jclass and jobject
JNIEXPORT void JNICALL Java_method(JNIEnv *env,jobject, jobject objArray)
{
int n = env->GetArrayLength(objArray);
for (int i = 0; i<n ; ++i)
{
jobject sObject = env->GetObjectArrayElement(objArray, i);
jclass sObjectClass = env->GetObjectClass(sObject);
dosomething(sObjectClass, sObject);
env->DeleteLocalRef(sObject);
env->DeleteLocalRef(sObjectClass);
}
Short answer:
Yes it is a right way. The call DeleteLocalRef is not necessary but it is useful if the objArray is big or if the function execution time is long.
Longer answer:
Oracle reference documentation states
Primitive types, such as integers, characters, and so on, are copied
between Java and native code. Arbitrary Java objects, on the other
hand, are passed by reference. The VM must keep track of all objects
that have been passed to the native code, so that these objects are
not freed by the garbage collector. The native code, in turn, must
have a way to inform the VM that it no longer needs the objects. In
addition, the garbage collector must be able to move an object
referred to by the native code.
So any object that native code use must be marked as eligible for garbage collection from native code point of view when native code does need the object anymore. JNI has two types of references - global and local references. The references retrieved from GetObjectArrayElement and GetObjectClass are local because:
All Java objects returned by JNI functions are local references.
VM automatically frees all local references when a native function returns. So it is not necessary to free these references by DeleteLocalRef in most cases because VM frees them automatically.
But if there are a lot of local references required in one function call or the call takes long time then it is worth to free them explicitly immediately when they are not needed and do not wait to function returns. Freeing helps VM to do better memory management.
How do you get a reference to a C++ object, from another C++ object, inside a Lua script? I don't really know how to summarize that in words properly, so let me elaborate with a Lua example first:
function doSomething()
compo = a:getComponent()
compo:setVariable(0)
end
a is a C++ object, and the function getComponent returns a pointer:
// inside A.h
Component* A::getComponent();
It seems the problem is that getComponent() is passing a copy of the Component object to Lua, instead of a reference. I come across the same problem with every function that returns a pointer, Lua cannot modify the original object.
Object a seems to be working correctly, if I modify a variable from within Lua, it's outcome is mirrored in C++. Both A and component are bound to Lua already, as well as the required methods.
Am I missing something syntactically or is there more to it than that?
I am using luabind, Lua 5.1, and MinGW. Thanks for any help in advance.
EDIT
Here is the luabind code. I summarized it because there's a bunch of other binds that have no relation to the problem:
luabind::class_<A>("A")
.def("getComponent", &A::getComponent)
Make a Lua wrapper for the "component" too. Then make a:getComponent() return the Lua object, not a real reference for the C++ object. Add any methods you need on that new wrapper object. If you have more "objects", rinse and repeat.
In short: for every object you want to manipulate from Lua, you will have to create a Lua wrapper. The only way around that is creating extra functions on the top level object, and calling those from Lua (a:setComponentVariable(0) instead of a:getComponent() + compo:setVariable(0)).
Is it possible that luabind checks, if a member function call to an exported class (object) is for a valid object?
lets assume that i have a Class called Actor exposed using luabind to lua. Im calling a lua function from C++ with an actor object as parameter. Now before the function finishes, a script write would put the actor object in a global lua reference to be accessed later.
Later on, the actor object is deleted from the C++ site, another function is called which tries to access the invalidated actor object (any method from it) - and obviously since it has been deleted, it results in a crash (access violation)
sample:
local myObjRef = nil
function doSomethingWithActor(actor)
-- save, still valid object
actor:Say("hello")
myObjRef = actor
end
function calledAfterActorWasDeleted()
--- will crash if the c++ object has been deleted meanwhile, works fine if it still exists
myObjRef:Say("Crash...")
end
A NIL check doesnt help here, is this something that can be checked on luabinds site? The functions are executed using lua_pcall(....) and the stacktrace shows the error at luabinds call.hpp results = maybe_yield(L, lua_gettop(L) - arguments, (Policies*)0);
If not, is there another solution how to make sure somebody who writes a script cannot create these issues?
Now before the function finishes, a script write would put the actor object in a global lua reference to be accessed later.
That right there is where your problem is coming from. If you want Lua code to own the object (that is, preserve the existence of this object), then you need to use Luabind mechanics to tell Luabind that you want to do that. Otherwise, if you pass a pointer to some Lua function, Luabind will assume that the function will not be trying to gain ownership of it.
If you want ownership to be shared between Lua and Luabind, then you should wrap your objects in a boost::shared_ptr, and use Luabind's smart pointer mechanisms to do this.
You could also simply segregate your scripts better. If you have some script that operates on a particular actor, then that script and any functions it contains should be destroyed (ie: lose all references to it) along with the object. This requires proper coding discipline on the C++ side. It will also require that you use Lua environments to properly encapsulate each instance of a script, so that they can't sneak things out via globals. Lastly, you will need to have C++ maintain total control over when scripts are called and when they aren't.
Otherwise, ownership is something your scripters are simply going to have to know about and be careful of. They can't treat C++ parameters like any old Lua value.
If exercising disciplined programming practice is not possible or practical for you, then you will simply have to not pass Lua the actual C++ object. Instead, you need to pass Lua some proxy object, which is a reference to the original. boost::weak_ptr is a good example of such an object (though you wouldn't pass it exactly to Lua). The proxy would forward calls to the actual object. If the object has been deleted, the proxy would detect this and fail or do nothing or whatever.
I solved my issue the following way:
When im about to delete an object, i iterate through all lua functions from C++ (i have them in a list, they are bound to specific actor objects each). Then i inspect each upvalue (global/local vars accessable to a function) - then i compare the userdata pointer with my object im about to delete - if they match (and their classes) and NIL the upvalue. Optionally, i could just remove that offending function because it would not work well anymore anyway.
So the next the time the function is called, im just getting a soft lua error "trying to access xxx a nil value..." - no more access violations.
I know people would say "dont use lua_getupvalue/lua_setupvalue - they are only for debugging!" - but there is actually no documented or spoken side effect - and in my case its perfectly safe and works well - also there isnt the issue with left over proxy objects i could not delete.
I'm having problems with luabind. I define a std::map to allocate objects created in lua. I publish this map as a global object in lua in this way:
luabind::globals(L)["g_SceneManager2D"] = this;
After that, this object is used into a function into lua, where many objects are created and inserted into this map. The problem comes when lua function ends and luabind returns the control to C++ side program, because automatically all contents of the map are lost.
I was looking for the error. I keep the lua context alive, so this object must exists.
Could you helpme??
Many thanks :)
I suggest use a shared_ptr<>(this) rather than raw this. boost::shared_from_this might help. Make sure your class is registered using Luabind too, and that the class_ is specified as held by a shared_ptr.
Another fun idea might be to make your Lua function just generate the "map" as a Lua table, return it, and you can iterate over it in C++ to build your std::map.
If I understand your problem correctly, it seems you are creating objects in Lua, which you then insert into the map (either through Lua or C++) and subsequently lose. Without some more code, it's hard to tell exactly what the problem is. However, I would first look to make sure that those objects are indeed being created (double check it) and then I would check to see that Lua isn't garbage collecting them. If Lua is indeed garbage collecting those objects, then you won't see them on the C++ side because they're, well, gone.
If it helps, I'm finishing up a project which does something similar. I had to create and retrieve C++ objects from Lua, but instead of creating the objects in Lua, I just called C++ functions to do it for me, sending any necessary data in the Lua call (bound by Luabind). Those (C++) functions indexed the objects by IDs into hash tables and the IDs were returned to Lua in case it needed to retrieve the object script-side for operations. This setup makes it easier (and safer) to handle memory stuff correctly and prevents Lua from garbage collecting your objects.