Android JNI Global Reference Subtleties - java

Ok so given the following steps taken from Cpp
Use jni to make a dictionary
Make the jobject of the dictionary returned into a globalref
Use jni to call a Java method that returns an object (we will call this object *A*)
Add *A* to the dictionary WITHOUT making the ref of *A* global
What is the lifespan of the *A*?
My expectations are as follows. The dictionary itself is global and so is protected from garbage collection, when I call the 'Add' method from jni *A* is passed 'back into java' and then the dictionary will hold a new reference to it, protecting it too from garbage collection. So I expect *A* to last as long as the dictionary (ignoring outside meddling).
Am I on the right track here? Thanks.

when I call the 'Add' method from jni A is passed 'back into java' and then the dictionary will hold a new reference to it, protecting it too from garbage collection.
No. You are conflating two properties of local and global references. A local reference is only valid for the duration of the JNI call it is created in. After that, it's invalid. If you want to use it again in a subsequent JNI call, make it a GlobalRef.
So I expect A to last as long as the dictionary (ignoring outside meddling).
Yes, but the reference itself has become invalid so you can't use it anyway.

Related

Deleting jobject references in object assignment JNI invocation API

I want to perform the following loop:
for(absolute_date CURRENT_DATE = START_DATE; CURRENT_DATE.COMPARE_TO(END_DATE) <= 0; CURRENT_DATE.SHIFT_SELF(TIMESTEP))
{
CURRENT_STATE = Propagator.PROPAGATE(CURRENT_DATE);
}
Where CURRENT_STATE is an object of class state. propagator::PROPAGATE() is a method that returns an object of class state.
My state class is effectively a class wrapper for a Java library class which i'm calling via JNI invocation API. The problem I'm having is that I want to delete the local java references withDeleteLocalRefto prevent memory leaks (especially important as I will be looping many thousands of times).
However, since DeleteLocalRef is called in my state class destructor, the reference to the java jobject is destroyed as the RHS of the assignment returns, thus making CURRENT_STATE invalid as it contains a reference to a jobject which has been deleted.
How do I avoid this?
#Wheezil
Regarding your first point - since I am using the invocation API i.e. creating a virtual machine inside C++ and calling Java functions, I don't think I need to convert to Global References (as all local references remain valid until the JVM is destroyed or until a thread is detached). In this case I am not detaching and re-attaching threads to the JVM so the local references never get deleted. The important thing for me is to make sure the local references get deleted within JVM.
Regarding second point- I have already inhibited copying by setting my copy constructors/assignment operators = delete. My issue is more specifically about how to ensure these references get deleted.
My state class looks like this:
state::state(JNIEnv* ENV)
{
this->ENV = ENV;
this->jclass_state = ENV->FindClass("/path/to/class");
this->jobject_state = nullptr;
}
state::~state()
{
if(DOES_JVM_EXIST())
{
ENV->DeleteLocalRef(this->jclass_state);
ENV->DeleteLocalRef(this->jobject_state); //PROBLEMATIC
}
}
state::state(state&& state_to_move)
{
this->ENV = state_to_move.ENV;
//move jobjects from mover => new object
this->jobject_state = state_to_move.jobject_state;
this->jclass_state = state_to_move.jclass_state;
}
state& state::operator =(state&& state_to_move)
{
this->ENV = state_to_move.ENV;
//move jobjects from mover => current object
this->jobject_state= state_to_move.jobject_state;
this->jclass_state = state_to_move.jclass_state;
return *this;
}
TO describe the problem I'm facing in more detail: the propagator::PROPAGATE() method returns a state object by value (currently stack allocated). As soon as this function returns, the following happens:
1) The move assignment operator is invoked. This sets the jobject_state and jclass_state members in the CURRENT_STATE object.
2) The destructor is invoked for the state instance created in the PROPAGATE() function. This deletes the local reference to jobject_state and thus the CURRENT_STATE Object no longer has a valid member variable.
Where to start... JNI is incredibly finicky and unforgiving, and if you don't get things just right, it will blow up. Your description is rather thin (please provide more detail if this doesn't help), but I can make a good guess. There are several problems with your approach. You are presumably doing something like this:
struct state {
state(jobject thing_) : thing(thing_) {}
~state() { env->DeleteLocalRef(thing); }
jobject thing;
}
The first issue is that storing local refs is dangerous. You cannot hang onto them beyond the current JNI frame. So convert them to global:
struct state {
state(jobject thing_) : thing(env->NewGlobalRef(thing_)) {
env->DeleteLocaLRef(thing_);
}
~state() { env->DeleteGlobalRef(thing); }
jobject thing;
}
The second issue is that jobject is basically like the old C++ auto_ptr<> -- really unsafe, because copying it leads to danging pointers and double-frees. So you either need to disallow copying of state and maybe only pass around state*, or make a copy-constructor that works:
state(const state& rhs) thing(env->NewGlobalRef(rhs.thing)) {}
This should at least get you on the right track.
UPDATE: Ddor, regarding local vs global references, this link describes it well: "Local references become invalid when the execution returns from the native method in which the local reference is created. Therefore, a native method must not store away a local reference and expect to reuse it in subsequent invocations." You can keep local references, but only under strict circumstances. Note that in particular, you cannot hand these to another thread, which it seems you are not doing. Another thing -- there are limits on the total number of local references that can be active. This limit is frustratingly underspecified, but it seems JVM-specific. I advise caution and always convert to global.
I thought that I had read somewhere, that you do not need to delete jclass because FindClass() always returns the same thing, but I'm having a hard time verifying this. In our code, we always convert the jclass to a global reference as well.
ENV->DeleteLocalRef(this->jclass_state);
I must admit ignorance about C++ move semantics; just make sure that the default copy ctor is not called and your jobject_state is not freed twice.
this->jobject_state = state_to_move.jobject_state;
If your move constructor is being called instead of the copy constructor or assignment, I don't know why you would be seeing a delete on the destruction of the temporary. As I said, I am not expert on move semantics. I've always had the copy-constructor create a new global. reference.
You can not do this:
this->ENV = ENV;
You are caching the JNIEnv value passed to native code from the JVM.
You can't do that.
OK, to be pedantic, there are some instances where you can, but that can only work from a single thread, so there's no need to cache a JNIEnv * value for later reference when it's used by the same thread.
From the complexity of what you've posted, I seriously doubt you can guarantee your native code gets called by the same thread every single time.
You get passed a JNIenv * every time native code gets called from the JVM, so there's pretty much never any point in caching the JNIEnv value.
IMO, you are making your native code way too complex. There's no need to be caching and tracking all those references. It looks like you're trying to keep a native C++ object synchronized with a Java object. Why? If native code needs access to a Java object, simply pass that object in the call from Java to the native code.

Must I DeleteLocalRef an object I have called NewGlobalRef on?

As we know, creating Java objects in a thread owned by C/C++ we are responsible to call DeleteLocalRef or Push/Pop LocalFrame as needed.
I have some code like
jbyteArray buffer = env->NewByteArray((jsize)requiredSize);
longTermBufferReference = (jbyteArray)env->NewGlobalRef(buffer);
where native code creates a byte array, which it re-uses across multiple threads/calls, once we are done with it, I call DeleteGlobalRef on the buffer.
The question is, must I call DeleteLocalRef on buffer when I'm done, or does the GlobalRef take complete ownership of the object?
In a garbage collected system, there is no such thing as object ownership. There are root object references and object references that are reachable directly or indirectly from the roots. (Weak references are not considered).
Both JNI local references and JNI global references are root references. Creating additional references does not affect existing references nor remove them from the list of root references.
So, yes, the local reference must be deleted (just as the global reference must be deleted). You already know that you can do it explicitly with DeleteLocalRef or PopLocalFrame, or, when a JNI native method returns, JNI effectively calls PopLocalFrame automatically.
To add to what #EJP has said: an object becomes eligible for garbage collection only when there are no (strong) references to it anymore. When you do NewGlobalRef you create a second reference to your object increasing the number of references to 2. The local reference will still be there.
It will be deleted by the runtime when your function exits, but suppose your function was a long one. If you called DeleteGlobalRef, you would delete the global reference, but the local reference would still be unaffected. Number of references: 1. Only after deleting this one too would the garbage collector be able to claim that object.
The question is, must I call DeleteLocalRef on buffer when I'm done
No, you can leave it to be deleted automatically when your JNI method returns, or by PopLocalFrame() if you use that. But local references are a scarce resource: if your JNI function uses a lot of them you should release them ASAP.

Does Java's pass-by-valueness mean I do not need a deepCopy function for my class?

I am having a rough time getting some of my code to work. The code starts a SwingWorker and gets the results. I am having trouble passing data to the worker and getting information back from the worker.
The data I want to pass is made up of objects of classes I defined myself. For example I have both an Item and Inventory object. The Item object contains basically all primitive types (name, price, etc) and the Inventory contains a LinkedList of Items.
I can't quite remember the series of events that lead up to my sanity check, but as a sanity check I implemented Item.getDeepCopy(Item inItem) and Inventory.getDeepCopy(Inventory) functions so that I could pass copies of Items and Inventorys to my workers. Is this needed?
How I use the deep copy functions is when I pass data to and get data from my StringWorkers. Say I launch a worker as a result of a button click. In the event handler I first get a deep copy of my classes private local copy of Inventory or Item and pass that to the workers constructor. Is this correct? Do I need to pass a deep copy? I think not..
I do this out of fear that the worker will try to modify the classes reference to the object within the worker itself causing some threading issues. But after some reading and critical thinking this couldn't be the case because java is pass-by-value, so what is passed to the worker cannot possibly lead to the GUI components data changing. Is this correct thinking?
Then when the worker is done, it calls an overridden done method I wrote. This method runs on the EDT so I can call functions from my GUI component, namely a function I call WorkerDone(boolean result, Inventory outInv). The worker calls this function and passes (not a deep copy) its local copy of Inventory or Item back to the GUI. When the GUI gets it it then performs a deep copy and sets its local Inventory or Item to this value. Is this a good use for the deep copy?
Edit: A little more.
Basically I want to pass "some data" to the worker and allow it to work on it with no link to the GUI components. When the worker is done it will either have finished successfully or not. If successful I want to get the data "back" from the worker and update my local copy with the data returned. I don't want the worker to "touch" any of the data in the GUI.
For mutable-ness. I want to be able to change the data within the object after it is created. This is how I build my application with this in mind. What I want is not non-mutable objects to keep things thread safe, I just don't want threads interacting. I want to pass the worker some data and basically "forget that I sent it" and then when the worker is done and it calls the GUI's workerDone method the GUI simply just agrees to set its local copy of the data to the value of the returned object if the worker says it was successful.
Edit 2:
Just for clearer understand of the phrases pass-by-value and pass-by-reference. What I think when I see pass-by-value. Say I want to pass an apple by value, to do this I would put my apple in a cloning machine that makes an exact clone of the apple same in every respect and pass that apple. Whomever is passed this cloned apple can do anything with it, and none of it affects my initial apple.
What I think of when I see pass-by-reference is that if I want to pass my apply by reference I write down where my apple is on a piece of paper and then pass that. Whomever receives this piece of paper can then come to where my apple is and take a bite of it.
So my confusion comes from "Java is pass-by-value", if it is, then why do I have to worry about my worker causing thread violations when operating on the value passed to it?
Java is pass by value, but when you pass an object, you are passing a reference, and just a copy of that reference. Both references, the original and copy still refer to the same set of values in the heap.
It's valid to worry about code you have no control over possibly modifying your objects, but you could perhaps wrap that object in another that cannot be changed, or specify an interface that has the extract methods but not set methods.
Having the GUI have it's own copy of data could be useful if you might update the data, and halfway through updating the data you display it. If you have no worry about such inconsistencies, you may just want to share the same reference between the gui code and non-gui code to keep your code simple (assuming your classes here are thread-safe).
"Pass by value" and "pass by reference" describe how function arguments are passed on the stack. They have nothing to do with what can or can't happen to objects on the heap. Suppose that we write code in some completely made-up programming language:
def foo(x) :
x = 5;
end
def bar() :
a = 3;
foo(a);
print a;
end
What will the function bar() print? The answer depends on whether the parameter x in foo() is pass-by-value or pass-by-reference. In a pass-by-value language (e.g., in Java), the value of the local variable a is passed when bar() calls foo(a). The foo() function can modify its own copy of that value, but it can not modify the caller's local variable, a.
In a pass-by-reference language, the address of the caller's local variable is passed. The x in foo becomes an alias for the a in bar(), and the assignment, x=5 changes the value of the variable a in bar.
Java is always pass by value: A Java function can never, ever, modify its caller's local variables.
Where people get confused is, a Java value can either be a primitive type (int, double, char, ...), or it can be an object reference.
public javaFoo(MyType x) {
x.setFrobber(true);
}
public javaBar() {
MyType mt = new MyType();
foo(mt);
System.out.println(mt.toString());
}
The variable x in javaFoo() is a different variable from the variable mt in javaBar(), and "pass-by-value" means that the javaFoo() function can not change mt. BUT both variables refer to the same object, and javaFoo() can modify the object.
"Pass by value" means that an object reference is passed. A copy of the original reference, not a copy of the object's data.
In other words, all your callees see the same object (and can change it at will).
Here's a good article from the Oracle Java docs on strategies for making an object "immutable":
http://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html

Methods that local values are created in JavaScript

I have been wondering how local values are creating and destroyed in JavaScript for example
function multiple(value){return value*2;}
when I called this function will a local variable be created value and then would it be destroyed?
Or would it affect garbage collection and become something to be removed?
And would that also apply to Java?
Please don't say premature optimization is the root of all evil, I know that already.
if this is a bad question please dont -rep me, just say so and I'll delete it
In javascript, value is an argument to the function and it will be created at the time the function is called. Another variable is then created as the result of the value*2 expression and that result is then returned from the function.
There are no local variables in the normal sense of that word since none are declared in that function. Arguments to the function work kind of like local variables (same scope, same default lifetime), but people don't normally call them local variables.
If the return value from that function is assigned to something as in:
var result = multiple(3);
Then, the return value will live on in the result variable. If the return value is not assigned and not used (which would be odd in this case), then it will have no references to it and will be freed by the the garbage collector.

what is global about the clsStr variable which is "clsStr = (*env)->NewGlobalRef(env,cls)"?

Global Reference in JNI is said to be a reference that has to be manually freed by the programmer. It has nothing to do with the c context. so a code like :
{
jclass clsStr = (*env)->NewGlobalRef(env,cls);
}
return clsStr;
will give an error saying that clsStr is undefined / undeclared. I understand this. But what i don't understand is the use of these type of references.
What is global about clsStr in the above code ? How this variable can be useful in future or after the call returns ? I also read that "Basically, the global reference is useful for ensuring that it (and its contents) will survive to the next JNI invocation" but i don't understand this.
It means that you're allowed to hold on to the reference you get from NewGlobalRef() across multiple calls to the native mathod. The reference will remain valid until you explicitly call DeleteGlobalRef().
This is in contrast to local references:
A local reference is valid only within the dynamic context of the native method that creates it, and only within that one invocation of the native method. All local references created during the execution of a native method will be freed once the native method returns.
If you store a global reference in a variable that's allowed to go out of scope before you call DeleteGlobalRef(), you leak memory. The following is an example of that:
{
jclass clsStr = (*env)->NewGlobalRef(env,cls);
}
Global Reference in JNI is said to be a reference that has to be
manually freed by the programmer. It has nothing to do with the c
context.
No it isn't. That is a terrible misquote from the JNI Specification. Here's what it really says:
The JNI divides object references used by the native code into two
categories: local and global references. Local references are valid
for the duration of a native method call, and are automatically freed
after the native method returns. Global references remain valid until
they are explicitly freed.
Nothing in JNI can alter the semantics of the C programming language.

Categories