Bug 19731 - When JNIEnv->FindClass throws ClassNotFoundException, include the name of the class in the exception
Summary: When JNIEnv->FindClass throws ClassNotFoundException, include the name of the...
Alias: None
Product: Android
Classification: Xamarin
Component: General ()
Version: 4.13.x
Hardware: PC Other
: Normal enhancement
Target Milestone: ---
Assignee: Jonathan Pryor
Depends on:
Reported: 2014-05-13 12:16 UTC by Timothy A. Graupmann
Modified: 2017-06-30 19:17 UTC (History)
3 users (show)

Tags: bb
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description Timothy A. Graupmann 2014-05-13 12:16:27 UTC
Feature Enhancement

I recently ran into a perplexing issue on Android running on a custom build of AOSP. I was using MonoGame and I was crashing with out of memory in the first 30 seconds of the application being launched.

The stack trace included 2000 entries of ClassNotFoundExceptions that occurred without logging the name of the missing class.

And since the exceptions were being thrown inside Xamarin, there was no way to figure out what Java class was missing.

So this is a feature request to include the name of the missing class in the exception so I'll be able to see it in the stack trace.

Even better would be to log the missing Java class as an error.

That would greatly help me identify whatever the missing class was.

FindClass is part of all bindings and so I figure it's the best way to categorize the issue.


~Tim Graupmann
Comment 1 Timothy A. Graupmann 2014-05-13 14:25:47 UTC
If you need a stack trace:

Comment 2 Jonathan Pryor 2017-06-30 19:17:58 UTC
Not all of those are stack traces, or can even be handled within Xamarin.Android.

> GREF has increased to 1901
> GREF has increased to 2001
> JNI global reference table (0x67465420) dump:
>   Last 10 entries (of 2001):
>      2000: 0x41cd31e0 java.lang.Class
>      1999: 0x42ffc2d8 java.lang.NoClassDefFoundError
> ...

These messages aren't printed out because a NoClassDefFoundError was thrown. These messages are printed out because:

1. *LOTS* of NoClassDefFoundError were thrown, and
2. The instances from (1) were all surfaced to managed code, and
3. (Presumably; I'm guessing here) "Something, somewhere" has a `catch (Exception)` block which is *ignoring*/hiding the exceptions from (1,2).


> Excessive JNI global references (2001)
> VM aborting
> Stacktrace:
>   at <0xffffffff>
>   at (wrapper managed-to-native) object.wrapper_native_0x407b4219 (intptr,intptr) <0xffffffff>
>   at Android.Runtime.JNIEnv.NewGlobalRef (intptr) <0x00043>
>   at Android.Runtime.JNIEnv.FindClass (string) <0x0020f>

*Within* Xamarin.Android, FindClass() returned a non-null IntPtr value. We then convert that into a JNI Global Reference via NewGlobalRef(), and Android *aborts the process*. We have no way of knowing that Android is going to abort, and thus no way of even printing out the class we were attempting to load from FindClass() -- even if we wanted to print that out, which usually we don't, as that would spam `adb logcat`.

> When JNIEnv->FindClass throws ClassNotFoundException, include the name of the class in the exception

We do, kind-of. If an exception instance is a ClassNotFoundException *and* you call ClassNotFoundException.ToString() (or Exception.ToString()), the class that couldn't be found *will be printed*.

For example:

  try { Java.Lang.Class.ForName ("this.type.does.not.exist"); }
  catch (Exception e) { Console.WriteLine (e); }

will print:

> Java.Lang.ClassNotFoundException: this.type.does.not.exist ---> Java.Lang.ClassNotFoundException: Didn't find class "this.type.does.not.exist" on path: DexPathList[[zip file "/data/app/scratch.bxc19731-1/base.apk"],nativeLibraryDirectories=[/data/app/scratch.bxc19731-1/lib/arm64, /system/fake-libs64, /data/app/scratch.bxc19731-1/base.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64]]

The problem you're observing is that *nothing* is calling ClassNotFoundException.ToString(). (Or, if something *is* calling it, those messages aren't making it onto your radar.) Instead, the exception is being caught and ignored. Our wrapper hangs around, consuming a GREF, which later results in Android aborting things because we run out of GREFs.

The wrappers will be collected at the next GC, assuming nothing is holding onto them.