Understanding NewGlobalRef/DeleteGlobalRef/DeleteLocalRef

9,345 views
Skip to first unread message

John Gaby

unread,
Sep 6, 2010, 10:18:10 AM9/6/10
to android-ndk
The reason I am trying to understand these functions is that I am
having a problem with my program crashing if I create too many
jobjects within a single call to my C code. This is not a permanent
memory leak, because if I create less than a certain number of objects
each call, I can make the call as many times as I want without
problem.

Now as I understand it, I have a limited number of local references
available to me (does anyone know what the limit is?), and I am
thinking that this the source of my problems. So I started using the
DeleteLocalRef call after I convert the jobject to a global
reference. This seems to have improved matters, in that I can now
create more objects within a single call, but is still crashes if I
create too many. Is there a way to determine how many local
references I currently have?

I am also trying to understand the exactly how NewGlobalRef works.
When I call a Java function that returns a jobject from my C code, it
is my understanding that I am given a local reference to the object,
and that if I want to save it for later use, I need to call
NewGlobalRef. When I do that, however, the pointer I get back is the
same as the pointer I pass in. Is this correct?

Does anyone have any guidance that can help me understand what is
going on here?

Thanks

Christian Linne

unread,
Sep 6, 2010, 11:01:23 AM9/6/10
to andro...@googlegroups.com
You may have a look into the official jni Programmer's Guide: http://java.sun.com/docs/books/jni/html/refs.html

In general, your problem appears to be a little curious. You do not create any object on calling C or C++ - code, you handle them over, making them accessible through native code. Normally, you only have local references to the JNIEnv, the object or class the call came from and to any object handled over - this may only hang if you're dealing with multiple threads accessing the same references, mostly results in a crash (because the references are not valid any longer).

And yes, it is correct that the same pointer is returned on NewGlobalRef, because it will not be modified internally. This is only a call to the VM which still controls the object to avoid that the reference will be garbage-collected when you leave the current thread.

Anyway, some code snippets would be really useful to get a better view of your problem.



2010/9/6 John Gaby <jg...@gabysoft.com>

--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To post to this group, send email to andro...@googlegroups.com.
To unsubscribe from this group, send email to android-ndk...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/android-ndk?hl=en.




--
_________
Mit freundlichen Grüßen
Yours sincerely

Christian Linne

Mail (private): linn...@aol.com
Mail (official): Christi...@googlemail.com,
ICQ: 293253013

andrej sarkic

unread,
Sep 6, 2010, 11:01:34 AM9/6/10
to android-ndk

John Gaby

unread,
Sep 6, 2010, 11:53:37 AM9/6/10
to android-ndk
First, let me say that I have read that documentation, and believe I
understand it.

The program is fairly complex, but I have tried to whittle down the
problem to something fairly simple. What I am trying to do is create
a C++ wrapper around the Java, so I can create pages and controls from
C++. In this particular case, I am creating a table which will have a
number of rows. Each row of the table consists of one 'GViewGroup'
object which is my class that extends ViewGroup. This object is
created as follows:

C++ Code:

jobject GApplicationImpl::CreateGroupView()
{
jobject ret = 0;
jclass cls = m_env->GetObjectClass(m_system);
jmethodID mid = m_env->GetMethodID(cls, "CreateGroupView", "()Lcom/
gabysoft/system/GViewGroup;");

if (mid != NULL)
{
jobject groupView;

if (groupView = m_env->CallObjectMethod(m_jobject, mid))
{
ret = m_env->NewGlobalRef(groupView);

m_env->DeleteLocalRef(groupView);
}
}

return(ret);
}

Java Code:

@SuppressWarnings("unused")
private GViewGroup CreateGroupView()
{
GViewGroup vg = new GViewGroup(m_activity);

return (vg);
}

Then I create GTextView objects (these are based on TextView) and
place them into the ViewGroup. These objects are created as follows:

C++ Code:

jobject GWindowImpl::CreateTextView(GRect& rect)
{
jobject ret = 0;
JNIEnv * env = m_pApplication->m_env;
jclass cls = env->GetObjectClass(m_groupView);
jmethodID mid = env->GetMethodID(cls, "CreateTextView", "(IIII)Lcom/
gabysoft/system/GTextView;");

if (mid != NULL)
{
jobject textView;

if (textView = env->CallObjectMethod(m_groupView, mid, rect.m_x,
rect.m_y, rect.m_width, rect.m_height))
{
ret = env->NewGlobalRef(textView);

env->DeleteLocalRef(textView);
}
}

return(ret);
}

Java Code:

@SuppressWarnings("unused")
private GTextView CreateTextView(int x, int y, int width, int height)
{
GTextView tv = new GTextView(this, m_context, x, y, x + width, y +
height);

this.addView(tv, x, y, width, height);

return(tv);
}

Now ordinarily I squirrel away the return from the CreateTextView into
my own data structures (note that I have added a global reference so
this should be ok, correct?) However as a test case I create a single
row of the table and then add multiple TextViews to it. The code for
this is as follows:

int cnt = 500;
GRect rect(0, 0, 100, 20);

while (cnt--)
{
jobject textView;
textView = CreateTextView(rect);
}

Now I know that this snippet produces a global memory leak, but
remember that this is only a test.

If cnt is set to 100, this code executes with no problem. If I set
cnt to 500, the program crashes on the return from the call to Java to
create the TextView with a 'SISEGV' received message.

Any help would be appreciated.

Thanks


On Sep 6, 8:01 am, Christian Linne <christian.li...@googlemail.com>
wrote:
> You may have a look into the official jni *Programmer's Guide*:http://java.sun.com/docs/books/jni/html/refs.html
> > android-ndk...@googlegroups.com<android-ndk%2Bunsu...@googlegroups.com>
> > .
> > For more options, visit this group at
> >http://groups.google.com/group/android-ndk?hl=en.
>
> --
> _________
> Mit freundlichen Grüßen
> Yours sincerely
>
> Christian Linne
>
> Mail (private): linne2...@aol.com
> Mail (official): Christian.Li...@googlemail.com,
> ICQ: 293253013

Christian Linne

unread,
Sep 7, 2010, 4:18:07 AM9/7/10
to andro...@googlegroups.com
I think you mean SIGSEGV, also known as segmentation fault. That may be caused by heap corruption.
The problem, as I see it, is the following:

 jobject textView;
 textView = CreateTextView(rect);

'jobject' itself is only a pointer, as defined in the jni.h , so you are always overwriting it with another one,

Now I know that this snippet produces a global memory leak, but
remember that this is only a test.
but it seems that you already know this, so you should better work on it. With this kind of overwriting, you are trashing the heap, which obviously causes the heap-corruption as mentioned above, which results in a SIGSEGV. There should be more information about this in the logcat - something like "heap corruption detected" or "libc called abort".

Just a general question - why are you trying to handle views in that complicated way ? You should note that you would jump over jni minimum twice each time you call a method on one of them (in most cases, more than four jumps will be made), which is a really bad idea if you are looking for performance. And a waste of memory, either.

2010/9/6 John Gaby <jg...@gabysoft.com>
To unsubscribe from this group, send email to android-ndk...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/android-ndk?hl=en.




--
_________
Mit freundlichen Grüßen
Yours sincerely

Christian Linne

alan

unread,
Sep 7, 2010, 4:45:16 AM9/7/10
to android-ndk
Ignore the above message its completely wrong, the following code is
equivalent to your code and other than the memory leak is completely
legal:
std::string * p;
while (true)
{
p = new std::string("");
}
the problem is in this line of code:
jclass cls = env->GetObjectClass(m_groupView);
cls is a local reference and wont be freed until your method returns
to java. As you are always using the same class you could look it up
once then store that in a global reference.

On Sep 7, 9:18 am, Christian Linne <christian.li...@googlemail.com>
> > <android-ndk%2Bunsu...@googlegroups.com<android-ndk%252Buns...@googlegroups.com>

John Gaby

unread,
Sep 7, 2010, 10:14:19 AM9/7/10
to android-ndk
YES! that is IT. Thanks you so very much. I didn't realize that I
needed to free 'cls'. And you are right, I should be saving it for re-
use so that I don't have to make that call each time.

One question about the class object. Will it always be the same for
objects of a specific class even of the objects are different? That
is, can I save the cls the first time I see any object of a specific
type and then use that later when I have other objects of that type?

Thanks again.

John

alan

unread,
Sep 7, 2010, 10:30:51 AM9/7/10
to android-ndk
thats a very complicated question, in most situations it should always
be the same object unless you have multiple class loaders. I hadn't
read your code closely enough though, I think doing a call to
GetObjectClass is probably quite cheap and may be cheaper and safe
than holding onto a global reference. I assumed you were doing a
FindClass which can be quite expensive and should be cached.
One time I think you do get multiple class loaders is if you attach a
thread to the jvm. I'm not sure what the effect of passing one class
to another in jni will be.
> ...
>
> read more »

alan

unread,
Sep 7, 2010, 10:34:57 AM9/7/10
to android-ndk
if you look in jni.h and look for types that derive from jobject:
typedef jobject jclass;
typedef jobject jthrowable;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jarray jobjectArray;
when one of these types are returned from a function it will generally
be a local reference to a java object unless stated otherwise in the
jni documentation

On Sep 7, 3:14 pm, John Gaby <jg...@gabysoft.com> wrote:
> ...
>
> read more »

John Gaby

unread,
Sep 7, 2010, 10:48:45 AM9/7/10
to android-ndk
Thanks for the info. I will look over my code and make sure I don't
have any other cases like this one. The jclass issue was huge,
however, since I retrieved a jclass object every time I made a call to
the Java layer, not just when I created the controls.

Thanks again for your help.
> ...
>
> read more »

fadden

unread,
Sep 7, 2010, 6:17:39 PM9/7/10
to android-ndk
On Sep 6, 8:01 am, Christian Linne <christian.li...@googlemail.com>
wrote:
> And yes, it is correct that the same pointer is returned on NewGlobalRef,
> because it will not be modified internally. This is only a call to the VM
> which still controls the object to avoid that the reference will be
> garbage-collected when you leave the current thread.

Actually, that's an artifact of the way the Dalvik VM happens to
work. It is entirely possible that the return value from NewGlobalRef
will have a different 32-bit pattern. (I say "32-bit pattern" and not
"pointer" because it doesn't have to be a pointer, either.)

In some implementations, you'll get a different 32-bit value every
time you call NewLocalRef on the same object. This is why you need to
use IsSameObject instead of just doing a pointer comparison.

There's some JNI reference remapping stuff that is compiled out (look
for USE_INDIRECT_REF in the sources) that will leave you very
surprised if you're expecting the same object to always be represented
the same way.

fadden

unread,
Sep 7, 2010, 6:21:22 PM9/7/10
to android-ndk
On Sep 7, 1:18 am, Christian Linne <christian.li...@googlemail.com>
wrote:
> I think you mean SIGSEGV, also known as segmentation fault. That may be
> caused by heap corruption.

They're also deliberately caused by the VM. If the fault address in
logcat is "deadd00d", you have annoyed Dalvik and can find an
explanation in logcat. If it's "deadbaad" then you probably irritated
dlmalloc() with heap corruption.

We use a self-inflicted segmentation fault because it shows up
distinctively in debuggerd's logcat output.

I'll add my usual plug for the "JNI Tips" document:

http://android.git.kernel.org/?p=platform/dalvik.git;a=blob_plain;f=docs/jni-tips.html;hb=HEAD
Reply all
Reply to author
Forward
0 new messages