3

When running the below code, the app crashes (after ~30 seconds) with the below stacktrace. I find this very odd since I would expect the garbage collector to clean up this memory. Our application has a similar pattern and crashes with a similar stacktrace.

Commenting out the line that instantiates the NSObject member makes the app run without crash. Commenting out the line that instantiates the byte array makes the app run MUCH longer, but it still crashes.

Instruments reports a pretty well constant Live Bytes for the app and instrumenting causes the app to run much longer without crashing, but it does still crash (after ~10 minutes). The constant Live Bytes makes me feel like the garbage collector is working.

Code:

using System.Threading;
using MonoTouch.Foundation;
using MonoTouch.UIKit;

namespace MyExample
{
    public class Application
    {
        static void Main (string[] args)
        {
            UIApplication.Main(args);
        }
    }

    public partial class AppDelegate : UIApplicationDelegate
    {
        public override bool FinishedLaunching (UIApplication app, NSDictionary options)
        {
            Thread testThread = new Thread(BreakMe);
            testThread.Start();

            window.MakeKeyAndVisible();
            return true;
        }

        private void BreakMe()
        {
            while(true)
            {
                using (var arPool = new NSAutoreleasePool())
                {
                    MyGarbage garbage = new MyGarbage();
                }
            }
        }

        private class MyGarbage
        {
            byte[] _Foo = new byte[100000];
            NSObject _Bar = new NSObject();
        }
    }
}

Application Output:

Mprotect failed at 0x493c000 (length 4096) with errno 12
Stacktrace:

  at (wrapper managed-to-native) System.Array.CreateInstanceImpl (System.Type,int[],int[]) <0xffffffff>
  at System.Array.CreateInstance (System.Type,int[]) <0x000bc>
  at System.Array.CreateInstance (System.Type,int) <0x00057>
  at System.MonoCustomAttrs.GetCustomAttributes (System.Reflection.ICustomAttributeProvider,System.Type,bool) <0x000db>
  at System.MonoCustomAttrs.GetCustomAttribute (System.Reflection.ICustomAttributeProvider,System.Type,bool) <0x00033>
  at System.Attribute.GetCustomAttribute (System.Reflection.MemberInfo,System.Type,bool) <0x0003f>
  at MonoTouch.ObjCRuntime.Class.GetHandle (System.Type) <0x00037>
  at MonoTouch.Foundation.NSObject.AllocIfNeeded () <0x00063>
  at MonoTouch.Foundation.NSObject..ctor (MonoTouch.Foundation.NSObjectFlag) <0x00027>
  at MonoTouch.Foundation.NSAutoreleasePool..ctor () <0x00037>
  at MyExample.AppDelegate.BreakMe () [0x00000] in Main.cs:30
  at (wrapper runtime-invoke) object.runtime_invoke_dynamic (intptr,intptr,intptr,intptr) <0x000cb>

Native stacktrace:

    0   MyExample                       0x002db308 mono_handle_native_sigsegv + 404
    1   MyExample                       0x002fa5dc sigabrt_signal_handler + 148
    2   libsystem_c.dylib                   0x369c972f _sigtramp + 42
    3   libsystem_c.dylib                   0x369be3bb pthread_kill + 58
    4   libsystem_c.dylib                   0x369b6bff abort + 78
    5   MyExample                       0x0041e484 GC_remap + 200
    6   MyExample                       0x00411ee4 GC_allochblk_nth + 1536
    7   MyExample                       0x00411894 GC_allochblk + 96
    8   MyExample                       0x0041d94c GC_new_hblk + 116
    9   MyExample                       0x00413c3c GC_allocobj + 188
    10  MyExample                       0x0041859c GC_generic_malloc_inner + 352
    11  MyExample                       0x004187ac GC_generic_malloc + 132
    12  MyExample                       0x00418c60 GC_malloc + 208
    13  MyExample                       0x003a67dc mono_object_allocate + 64
    14  MyExample                       0x003a7240 mono_array_new_full + 828
    15  MyExample                       0x00341324 ves_icall_System_Array_CreateInstanceImpl + 896
    16  MyExample                       0x0012cf3c (wrapper managed-to-native) System.Array:CreateInstanceImpl (System.Type,int[],int[]) + 80
    17  MyExample                       0x0012d23c System.Array:CreateInstance (System.Type,int) + 88
    18  MyExample                       0x0018b70c System.MonoCustomAttrs:GetCustomAttributes (System.Reflection.ICustomAttributeProvider,System.Type,bool) + 220
    19  MyExample                       0x0018b560 System.MonoCustomAttrs:GetCustomAttribute (System.Reflection.ICustomAttributeProvider,System.Type,bool) + 52
    20  MyExample                       0x00131fd0 System.Attribute:GetCustomAttribute (System.Reflection.MemberInfo,System.Type,bool) + 64
    21  MyExample                       0x000795ec MonoTouch.ObjCRuntime.Class:GetHandle (System.Type) + 56
    22  MyExample                       0x00077e60 MonoTouch.Foundation.NSObject:AllocIfNeeded () + 100
    23  MyExample                       0x0007779c MonoTouch.Foundation.NSObject:.ctor (MonoTouch.Foundation.NSObjectFlag) + 40
    24  MyExample                       0x00074d10 MonoTouch.Foundation.NSAutoreleasePool:.ctor () + 56
    25  MyExample                       0x00002c34 MyExample.AppDelegate:BreakMe () + 164
    26  MyExample                       0x001f3e3c (wrapper runtime-invoke) object:runtime_invoke_dynamic (intptr,intptr,intptr,intptr) + 204
    27  MyExample                       0x002c4658 mono_jit_runtime_invoke + 3032
    28  MyExample                       0x003a34a8 mono_runtime_invoke + 140
    29  MyExample                       0x003a48f0 mono_runtime_delegate_invoke + 136
    30  MyExample                       0x003cb31c start_wrapper + 752
    31  MyExample                       0x003f09a0 thread_start_routine + 240
    32  MyExample                       0x0041f9ac GC_start_routine + 132
    33  libsystem_c.dylib                   0x369be311 _pthread_start + 248
    34  libsystem_c.dylib                   0x369bfbbc start_wqthread + 0
tohlsen
  • 58
  • 4
  • Just tested the same exact code and it doesn't crash. However, apply the `NSAutoreleasePool` **once** in the thread. Then enter the `while` code inside the `using` block. – Dimitris Tavlikos Mar 25 '11 at 08:20
  • I should've qualified that it never crashes in the iPad simulator and I'm running on an iPad 1 with iOS 4.3. – tohlsen Mar 25 '11 at 12:59
  • @Dimitris is right, your code is flawed: no using statement and possible needing NSAutoreleasePool. Make his corrections and see if it fixes your issue. The simulator is very different from the device as it has access to more RAM, faster CPU, etc. Memory problems will occur quicker on the device. – jonathanpeppers Mar 25 '11 at 13:02
  • @Dimitris, the way I understand th NSAutoreleasePool, I would want the pool to be disposed inside the while loop in order for the memory to be collected by GC. – tohlsen Mar 25 '11 at 13:03

2 Answers2

2

The true answer to this is, is suspect, the issue resolved in Why is our MonoTouch app breaking in the garbage collector? It is not out of memory which was a memory manager issue. The stack trace is very familiar to me.

Community
  • 1
  • 1
mj2008
  • 6,647
  • 2
  • 38
  • 56
  • I find it very frustrating that we spent weeks trying to figure this out (thinking it was us using MonoTouch incorrectly) when it was a problem with MonoTouch itself. And I'm also frustrated that it took someone external to Novell to point out in their own source code where the problem lies. Sorry for venting, but I'm hopeful this note will be heard that MonoTouch NEEDS to support their platform (more than just a forum). – tohlsen May 12 '11 at 15:37
  • Running tests today to verify this is in fact the issue we were having. With MonoTouch 3.x, even after injecting GC.Collect throughout our code (and killing performance) we were still having stability issues. I have taken these out and upgraded to MonoTouch 4.0.3. More on the results tomorrow... – tohlsen May 12 '11 at 15:38
  • It is fair to say that we dug down into this including the MonoTouch bug database, and other reports. Geoff was very good once we were able to give him a reproducable failure, even it it did take hours to happen. If it is stable, feel free to accept my answer instead. 8-) – mj2008 May 12 '11 at 16:24
  • The original example in this post looks to be completely fixed in MonoTouch 4.0.3. When we ramp our app up to the maximum amount of data through our stream our app ran for 6 hours before crashing, which is a phenomenal difference than the ~10 minutes it used to run when we ramped up. We still have issues to work out, but I feel much more confident that the problem lies in our usage than the framework itself. Very nice work @mj2008 and @Geoff fixing this bug! – tohlsen May 13 '11 at 13:52
1

You have put the allocator in a race with the garbage collector. If you make a simple change to your demo and add:

System.GC.Collect ();

In the loop, you will see it no longer crashes.

Whats happening here is you are allocating as fast as possible. When the GC runs out of memory its expanding the heap and collecting. The next few times the loop runs it runs a bit longer before having to collect again due to the expaned heap, but eventually the race is lost.

Making the minor modification I stated above allowed the test to run for 10 minutes here before I gave up.

Geoff Norton
  • 5,056
  • 1
  • 19
  • 17
  • Thank you so much for not only giving the answer, but explaining why. Nice work and again, thank you! – tohlsen Mar 28 '11 at 13:13
  • Why can't MonoTouch run garbage collection inside the allocator when the allocator has run short? It seems like without it doing that, the only way to ensure that it'll -never- crash in an allocation is to call GC.Collect before every allocation. I have an app on the store that is plagued by random GC_remap crashes even though it is very well-behaved in terms of the objects that it keeps references to (i.e. it's always referencing roughly the same amount of memory even though it allocates frequently). – Mike Apr 09 '11 at 00:55
  • It does, but there are a number of other factors at play. How many objects are in the finalization queue? Have the finalizers had time to run? etc. – Geoff Norton Apr 09 '11 at 19:32