Skiasharp: (XF/Android) Exception occurs when GC collects : "System.ObjectDisposedException: Cannot access a disposed object."

Created on 9 May 2017  路  13Comments  路  Source: mono/SkiaSharp

Hi Matthew,
Could you look this exception?
When GC collects to make a room (automatically), this exception occurs and app crashes only on XF/Android.

I'm using SKCanvasView at many places.
I found this because the crash report tool received this crash from many users since I released new update SEVERAL DAYS ago. (It's released with SkiaSharp.1.57.0, and before release was on Feb)

skiasharp ver : 1.57.1
XF ver : 2.3.4.231

(UPDATE)
Unfortunately, I tested with 1.56.0 which was included on my Feb release, but the same exception occurs. I guess XF changed how GC handles or other problem.

In my new update, SkCanvas Views are used at many more places, where ListViewCell, ModalPage, CarouselView on Modal, etc.
And Xamarin.Android and I changed even Android SDK since Feb release, so many things are changed.
It's hard to find the reason.

[mono] Unhandled Exception:
[mono] System.ObjectDisposedException: Cannot access a disposed object.
[mono] Object name: 'Android.Graphics.Bitmap'.
[mono]   at Java.Interop.JniPeerMembers.AssertSelf (Java.Interop.IJavaPeerable self) [0x00030] in /Users/builder/data/lanes/4468/b16fb820/source/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.cs:153 
[mono]   at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeAbstractVoidMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x00002] in /Users/builder/data/lanes/4468/b16fb820/source/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods_Invoke.cs:11 
[mono]   at Android.Graphics.Bitmap.Recycle () [0x00000] in /Users/builder/data/lanes/4468/b16fb820/source/monodroid/src/Mono.Android/platforms/android-25/src/generated/Android.Graphics.Bitmap.cs:975 
[mono]   at SkiaSharp.Views.Android.SKCanvasView.FreeBitmap () [0x0000b] in <0008caffa6d54ab5af7bddda1c82b23a>:0 
[mono]   at SkiaSharp.Views.Android.SKCanvasView.Dispose (System.Boolean disposing) [0x00007] in <0008caffa6d54ab5af7bddda1c82b23a>:0 
[mono]   at Java.Lang.Object.Finalize () [0x00051] in /Users/builder/data/lanes/4468/b16fb820/source/xamarin-android/src/Mono.Android/Java.Lang/Object.cs:67 
[mono-rt] [ERROR] FATAL UNHANDLED EXCEPTION: System.ObjectDisposedException: Cannot access a disposed object.
[mono-rt] Object name: 'Android.Graphics.Bitmap'.
[mono-rt]   at Java.Interop.JniPeerMembers.AssertSelf (Java.Interop.IJavaPeerable self) [0x00030] in /Users/builder/data/lanes/4468/b16fb820/source/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.cs:153 
[mono-rt]   at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeAbstractVoidMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x00002] in /Users/builder/data/lanes/4468/b16fb820/source/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods_Invoke.cs:11 
[mono-rt]   at Android.Graphics.Bitmap.Recycle () [0x00000] in /Users/builder/data/lanes/4468/b16fb820/source/monodroid/src/Mono.Android/platforms/android-25/src/generated/Android.Graphics.Bitmap.cs:975 
[mono-rt]   at SkiaSharp.Views.Android.SKCanvasView.FreeBitmap () [0x0000b] in <0008caffa6d54ab5af7bddda1c82b23a>:0 
[mono-rt]   at SkiaSharp.Views.Android.SKCanvasView.Dispose (System.Boolean disposing) [0x00007] in <0008caffa6d54ab5af7bddda1c82b23a>:0 
[mono-rt]   at Java.Lang.Object.Finalize () [0x00051] in /Users/builder/data/lanes/4468/b16fb820/source/xamarin-android/src/Mono.Android/Java.Lang/Object.cs:67 
area-SkiaSharp.Views os-Android type-bug

Most helpful comment

Hi @benevbright I will look into this. I where the problem starts, my dispose method is trying to release the bitmap - which for some reason is already released. This technically should not happen since after releasing, the bitmap is set to null.

Can you provide more info as to what is causing the issue? Maybe you are scrolling a list view and then this happens? Is it during some page navigation? I need to be able to do some test - it may be that something is happening on another thread, and then the view is being disposed of twice simultaneously.

The finalizer is the result of the GC, but if this view was cleaned up in any way, my field will be null. If the bitmap is being cleaned up somewhere then something is still wrong as I still have my field referencing it. It might be that the GC is collecting both at the same time, which is weird still.

Any further info as what is happening at the time of the crash will be helpful.

All 13 comments

Hi @benevbright I will look into this. I where the problem starts, my dispose method is trying to release the bitmap - which for some reason is already released. This technically should not happen since after releasing, the bitmap is set to null.

Can you provide more info as to what is causing the issue? Maybe you are scrolling a list view and then this happens? Is it during some page navigation? I need to be able to do some test - it may be that something is happening on another thread, and then the view is being disposed of twice simultaneously.

The finalizer is the result of the GC, but if this view was cleaned up in any way, my field will be null. If the bitmap is being cleaned up somewhere then something is still wrong as I still have my field referencing it. It might be that the GC is collecting both at the same time, which is weird still.

Any further info as what is happening at the time of the crash will be helpful.

Hi, Matthew.
Thanks for your help.

I made a repro sample project which has a similar layout as my project.
I attach it here.
test_skiasharp_disposedException.zip

The difference between the sample and my project is that it's really hard to reproduce this exception.
To reproduce: try to view the Listview page and modal many time, after that, change the mainpage by using a button as many as you can. I got the exception after I press changing page button about 100 times.
And it's same to reproduce with our app. It occurs when I change my MainPage(when user login/logout). Our app is huge now, so I guess changing MainPage triggers GC to collect.

CLUE : likely it may happen when SkiaCanvas's isVisible property to 'false'.

Tested on Xamarin.Forms 2.3.4.231 / SkiaSharp 1.57.1 / Mac Android Player(Nexus 4) / Android SDK API 25.

But because It's too hard to reproduce, I can not say it's a problem of SkiaSharp.
But It occurs easily on our app than sample project.

[mono] Unhandled Exception:
[mono] System.ObjectDisposedException: Cannot access a disposed object.
[mono] Object name: 'Android.Graphics.Bitmap'.
[mono]   at Java.Interop.JniPeerMembers.AssertSelf (Java.Interop.IJavaPeerable self) [0x00030] in /Users/builder/data/lanes/4468/b16fb820/source/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.cs:153 
[mono]   at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeAbstractVoidMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x00002] in /Users/builder/data/lanes/4468/b16fb820/source/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods_Invoke.cs:11 
[mono]   at Android.Graphics.Bitmap.Recycle () [0x00000] in /Users/builder/data/lanes/4468/b16fb820/source/monodroid/src/Mono.Android/platforms/android-25/src/generated/Android.Graphics.Bitmap.cs:975 
[mono]   at SkiaSharp.Views.Android.SKCanvasView.FreeBitmap () [0x0000b] in <0008caffa6d54ab5af7bddda1c82b23a>:0 
[mono]   at SkiaSharp.Views.Android.SKCanvasView.Dispose (System.Boolean disposing) [0x00007] in <0008caffa6d54ab5af7bddda1c82b23a>:0 
[mono]   at Java.Lang.Object.Finalize () [0x00051] in /Users/builder/data/lanes/4468/b16fb820/source/xamarin-android/src/Mono.Android/Java.Lang/Object.cs:67 
[mono-rt] [ERROR] FATAL UNHANDLED EXCEPTION: System.ObjectDisposedException: Cannot access a disposed object.
[mono-rt] Object name: 'Android.Graphics.Bitmap'.
[mono-rt]   at Java.Interop.JniPeerMembers.AssertSelf (Java.Interop.IJavaPeerable self) [0x00030] in /Users/builder/data/lanes/4468/b16fb820/source/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.cs:153 
[mono-rt]   at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeAbstractVoidMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x00002] in /Users/builder/data/lanes/4468/b16fb820/source/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods_Invoke.cs:11 
[mono-rt]   at Android.Graphics.Bitmap.Recycle () [0x00000] in /Users/builder/data/lanes/4468/b16fb820/source/monodroid/src/Mono.Android/platforms/android-25/src/generated/Android.Graphics.Bitmap.cs:975 
[mono-rt]   at SkiaSharp.Views.Android.SKCanvasView.FreeBitmap () [0x0000b] in <0008caffa6d54ab5af7bddda1c82b23a>:0 
[mono-rt]   at SkiaSharp.Views.Android.SKCanvasView.Dispose (System.Boolean disposing) [0x00007] in <0008caffa6d54ab5af7bddda1c82b23a>:0 
[mono-rt]   at Java.Lang.Object.Finalize () [0x00051] in /Users/builder/data/lanes/4468/b16fb820/source/xamarin-android/src/Mono.Android/Java.Lang/Object.cs:67 

My users report the same issue.

I have the same issue with Mapsui from pauldendulk which uses SkiaSharp. It looks like this exception comes always when a huge amount of memory is allocated. I got the exception everytime when my app uses a lot of RAM. Even if I navigate away from the Map which is the only usage of SkiaSharp, this exception comes up as soon as I hit about 2.5-3.2GB RAM usage (depends on the amount of background processes). I tried to force the cleanup from the memory at the right moment but it dosn't change a bit.

Edit: This problem only occures on Android. On iOS the app runs fine, so I guess the cleanup works there.

[0:] AppDomain.CurrentDomain.UnhandledException: System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Android.Graphics.Bitmap'.
  at Java.Interop.JniPeerMembers.AssertSelf (Java.Interop.IJavaPeerable self) [0x00029] in <bd30a18775d94dc8b6263aecd1ca9077>:0 
  at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeAbstractVoidMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x00000] in <bd30a18775d94dc8b6263aecd1ca9077>:0 
  at Android.Graphics.Bitmap.Recycle () [0x0000a] in <d855bac285f44dda8a0d8510b679b1e2>:0 
  at SkiaSharp.Views.Android.SKCanvasView.FreeBitmap () [0x0000b] in <a8778ebdc67a470582197fdb95ec7e8c>:0 
  at SkiaSharp.Views.Android.SKCanvasView.Dispose (System.Boolean disposing) [0x00007] in <a8778ebdc67a470582197fdb95ec7e8c>:0 
  at Java.Lang.Object.Finalize () [0x00048] in <d855bac285f44dda8a0d8510b679b1e2>:0 . IsTerminating: True
In mgmain JNI_OnLoad
06-01 17:18:29.090 E/mono-rt (15355): [ERROR] FATAL UNHANDLED EXCEPTION: System.ObjectDisposedException: Cannot access a disposed object.
06-01 17:18:29.090 E/mono-rt (15355): Object name: 'Android.Graphics.Bitmap'.
06-01 17:18:29.090 E/mono-rt (15355):   at Java.Interop.JniPeerMembers.AssertSelf (Java.Interop.IJavaPeerable self) [0x00029] in <bd30a18775d94dc8b6263aecd1ca9077>:0 
06-01 17:18:29.090 E/mono-rt (15355):   at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeAbstractVoidMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x00000] in <bd30a18775d94dc8b6263aecd1ca9077>:0 
06-01 17:18:29.090 E/mono-rt (15355):   at Android.Graphics.Bitmap.Recycle () [0x0000a] in <d855bac285f44dda8a0d8510b679b1e2>:0 
06-01 17:18:29.090 E/mono-rt (15355):   at **SkiaSharp.Views.Android.SKCanvasView.FreeBitmap** () [0x0000b] in <a8778ebdc67a470582197fdb95ec7e8c>:0 
06-01 17:18:29.090 E/mono-rt (15355):   at **SkiaSharp.Views.Android.SKCanvasView.Dispose** (System.Boolean disposing) [0x00007] in <a8778ebdc67a470582197fdb95ec7e8c>:0 
06-01 17:18:29.090 E/mono-rt (15355):   at Java.Lang.Object.Finalize () [0x00048] in <d855bac285f44dda8a0d8510b679b1e2>:0 

Just a note about repro:

I managed to repro the easily by using GC.Collect();
I used the sample app provided by @benevbright, and then added a new button that called the GC. I opened the modal, scrolled, returned to the test5Page and called the GC.

It failed every time after the third collect.

The reason for this is that the finalizer is collecting the bitmap underneath me. When I call Recycle, the handle is zero, thus it throws. I am trying to find a solution - might be to just check the handle before using the bitmap.

The new dispose logic is this

if (bitmap != null)
{
    // the GC might have nuked our bitmap :(
    if (bitmap.Handle != IntPtr.Zero && !bitmap.IsRecycled)
        bitmap.Recycle();
    bitmap.Dispose();
    bitmap = null;
}

Although a bit scary, at least the GC is releasing the bitmap :)

Just pushed out a release that should resolve this: https://github.com/mono/SkiaSharp/releases/tag/v1.58.1

Always thanks, Matthew.
I wish I could have found the code causes the problem.
I will try the update!

Cool! Thanks a lot!

Have exactly the same problem in my code.
Have even synchronized it - still getting this totally strange ObjectDisposedException.

@Tsukrov Can you provide a stack trace and details about the version of forms, the device and anything else that you may be doing differently?

Reopening as this is still an issue. I will investigate this further.

Hi Matthew,

i encountered a similar problem but with the SKDocument object, more specifically the underlying Stream.
I create a fairly big Pdf document and it seems when the RAM limit is reached and the GC kicks in, it crashes afterwards. I use SkiaSharp version 1.59.1.

09-08 08:45:02.485 D/Mono    ( 3718): Searching for 'sk_document_close'.
09-08 08:45:02.485 D/Mono    ( 3718): Probing 'sk_document_close'.
09-08 08:45:02.485 D/Mono    ( 3718): Found as 'sk_document_close'.
09-08 08:45:02.593 I/art     ( 3718): Starting a blocking GC Explicit
09-08 08:45:02.599 I/art     ( 3718): Explicit concurrent mark sweep GC freed 11000(469KB) AllocSpace objects, 1(40KB) LOS objects, 60% free, 661KB/1685KB, paused 281us total 5.660ms
09-08 08:45:02.599 D/Mono    ( 3718): GC_TAR_BRIDGE bridges 3 objects 6 opaque 0 colors 3 colors-bridged 3 colors-visible 3 xref 0 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.02ms tarjan 0.01ms scc-setup 0.01ms gather-xref 0.01ms xref-setup 0.01ms cleanup 0.12ms
09-08 08:45:02.599 D/Mono    ( 3718): GC_BRIDGE: Complete, was running for 6.22ms
09-08 08:45:02.599 D/Mono    ( 3718): GC_MINOR: (Nursery full) time 1.59ms, stw 2.59ms promoted 206K major size: 1056K in use: 280K los size: 13312K in use: 10956K
09-08 08:45:02.621 D/Mono    ( 3718): DllImport searching in: 'libSkiaSharp.so' ('libSkiaSharp.so').
09-08 08:45:02.621 D/Mono    ( 3718): Searching for 'sk_paint_delete'.
09-08 08:45:02.621 D/Mono    ( 3718): Probing 'sk_paint_delete'.
09-08 08:45:02.622 D/Mono    ( 3718): Found as 'sk_paint_delete'.
InspectorDebugSession(106): HandleTargetEvent: UnhandledException
Unhandled Exception:

System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'SKManagedStream: -1736448144'.

I could provide some repro demo project if its desired.

Was this page helpful?
0 / 5 - 0 ratings