-
Notifications
You must be signed in to change notification settings - Fork 6
Smart References
In raw JNI references to Java objects come in 4 flavors
- Automatic. These are local references (see next type below) that are owned by the currently active reference frame. For this to work someone needs to establish such frame. This is always done for you by JNI prior to calling a native method implementation, so anything called from there will operate on this same frame unless you create your own ones in-between. You do not not need to manage automatic references yourself (hence the name) but relying on the current frame is extremely dangerous. The size of the frame is fixed and can be pretty small. Since you share the frame with all the callers above you it is very easy to run out of space there, especially if you do something in a loop. The main advantage of automatic references is that they are deallocated in-bulk offering potential speed savings. Each non-automatic local reference needs to be deallocated via its own JNI call. (For those familiar with old pre-ARC Objective C, automatic references and reference frame behave very similarly to autorelease pool and share the same gotchas)
- Local. This is the type of references to any new object created by JNI. They are local to current call and cannot be used outside of it. In other words you can pass them to functions you call and they do the same in turn but you can not store them for later use elsewhere. If there is a reference frame active when a local reference is created it will track the reference and dispose of it automatically. In other words such local reference will be automatic. However, due to the issues with automatic references mentioned above you should manually dispose of them.
- Global. You can convert any local reference to a global one. Global references can be stored for later use. However, most JNI implementations have some kind of limit to how many global references there can be in total. The limit is usually much larger than automatic reference frame one but not infinite. Trying to "keep" too many Java objects in C++ code via global reference can run out of this limit. For this reason it is better to limit use of global reference to a few key objects, if any.
- Weak. A kind of global reference that does not prevent garbage collection of the referent.
SimpleJNI follows the philosophy of being safe by default and so it avoid the use of automatic references by default.
References in SimpleJNI are represented by "smart reference" objects. These are RAII wrappers over the raw JNI references. These are
auto_java_ref<T>
local_java_ref<T>
global_java_ref<T>
weak_java_ref<T>
local_java_ref
is by far the most common type you will encounter. Any SimpleJNI method that returns Java objects will produce local_java_ref<jSomething>
instead of raw jSomething
. When this object goes out of scope it will manually delete the local reference. Yes, it is potentially "slower" than relying on a frame but it is also much safer. You can still get the frame semantics if you want, but you will need to manually indicate so. In other words safe behavior - default; unsafe, but potentially faster behavior - manually invoked. The following example illustrates various usage patterns
jSomething foo(JNIEnv * env)
{
//Create new object. You can use auto for return type
java_local_ref<jSomething> obj = java_classes::get<ClassOfSomething>().ctor(env);
//Pass obj to a method that expects raw jSomething
bar(obj.c_ptr());
//Return obj as raw jSomething
return obj.release();
}
jSomething foo_advanced(JNIEnv * env)
{
//Create new object. We want to rely on automatic frame here
jSomething obj = java_classes::get<ClassOfSomething>().ctor(env).release();
//Pass obj to a method that expects raw jSomething
bar(obj);
//Return obj
return obj;
}
Note that java_local_ref
instances must not outlive the scope in which they were created. It is almost always an error to see them as class members (unless the class they are member of also lives only within the scope it is created) or global variables.
There are 2 methods on java_local_ref
that return raw pointer c_ptr()
and release()
. The first one produces "raw" C pointer as the name indicates. It should be used to pass the object to any method you call that expects a raw pointer. The release()
method relinquishes ownership of the pointer. It should be used to return the object as a raw pointer.
Just like any other smart pointer java_local_ref
can be assigned (to another java_local_ref
or different smart reference types) and checked for being not nullptr
via
if (obj)
{}
//and
if (!obj)
{}
The java_global_ref
and java_weak_ref
behave identically to java_local_ref
but manage global and weak JNI references respectively. They are never produced by any SimpleJNI call - you need to manually create them. Unlike java_local_ref
these can be class instance variables.
The java_weak_ref
behaves somewhat differently to other smart reference classes. Despite the fact that it's template argument is T when you extract raw pointer from it you will get a jweak
object. When you convert it to bool
you will check whether jweak
stored inside is nullptr
or not. In order to actually access the Java object you will need to convert it to one of the strong pointer classes and check that the result is not null.
All 4 smart reference classes support automatic conversions to each other. Conversions always work when the template argument T is the same. When the argument is not the same the conversions will work if java conversion between two types is defined as described in Declaring Java Types#conversions
On occasion you might want to write a method that accepts either local or global smart reference class without caring what it is. In such situation you could of course fall back on raw jSomething but doing so would prevent you from using automatic conversions facility described above. Instead you can use auto_java_ref
. This smart reference class participates in all the conversions but does not perform any reference management of its own. Instead it assumes that someone else, above in the calling hierarchy owns the object and will dispose of it as needed. Consider
DEFINE_JAVA_TYPE(jHashMap, "java.util.HashMap");
DEFINE_JAVA_TYPE(jMap, "java.util.Map");
DEFINE_JAVA_CONVERSION(jHashMap, jMap);
void bar(const java_auto_ref<jMap> & map)
{
...use map...
}
void foo()
{
local_java_ref<jHashMap> local = java_classes::get<ClassOfHashMap>().ctor();
bar(local);
global_java_ref<jHashMap> global = local;
bar(global);
}
Note that there are no explicit cast calls anywhere in the code above.
In order to obtain smart references from raw ones you can use the following calls
template<typename T>
auto_java_ref<T> jauto(T ptr) noexcept;
//Creates a new local reference.
template<typename T>
local_java_ref<T> jref(JNIEnv * env, T ptr) noexcept;
//Attaches an object to an existing local reference.
template<typename T>
local_java_ref<T> jattach(JNIEnv * env, T ptr) noexcept;
//Creates a new global reference.
template<typename T>
global_java_ref<T> jglobal_ref(T ptr);
//Attaches an object to an existing global reference.
template<typename T>
global_java_ref<T> jglobal_attach(T ptr);
//Creates a new weak reference.
template<typename T>
weak_java_ref<T> jweak_ref(T ptr);
//Attaches an object to an existing weak reference.
template<typename T>
weak_java_ref<T> jweak_attach(T ptr);
If you do want use local reference frames SimpleJNI provides a RAII wrapper to manage them.
void foo(JNIEnv * env)
{
//Let's use a simple frame that will wipe everything when destroyed
java_frame frame(env, 64); //size of the frame
...use up to 64 automatic references here...
}
jSomething foo(JNIEnv * env)
{
//Let's use a frame that will return an object at the end
java_frame frame(env, 64); //size of the frame
...use up to 64 automatic references here...
jSomething obj = ....;
//Clear the frame but retain one object
return frame.pop(obj);
}
- Building
-
User's Guide
Declaring Java Types
Accessing Methods and Fields
Representing Java Classes
Implementing Native Methods
Smart References
Error Handling
Obtaining JNIEnv
Initialization
Strings
Arrays
Direct Buffers
Booleans
Sizes -
JniGen Code Generator
Integrating JniGen
Annotations
Processor Options