Search code examples
objective-cglkit

Creating autoreleased object inside GLKViewController run loop causes malloc error


I'm currently using GLKit to do some OpenGL drawing. I created a normal UIViewController and then added a GLKViewController subclass inside a container to do my drawing. While everything runs fine initially, if I let my program run for a period of time (perhaps 15-30 minutes), eventually it crashes and gives me the following error.

malloc: *** mmap(size=2097152) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug

So I turned on the malloc_error_break breakpoint, and the stack trace points to the following code.

-(NSArray*)meshes:(NSArray *)meshes sortedFromFrontToBack:(BOOL)sortedFromFrontToBack
{
    NSMutableArray *sortedMeshes = meshes.mutableCopy;
    [sortedMeshes sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        DSNode *mesh1 = obj1;
        DSNode *mesh2 = obj2;
        GLKVector3 depth1 = isnan(mesh1.boundingSphere.radius) ? mesh1.transformationState.position : mesh1.boundingSphere.center;
        GLKVector3 depth2 = isnan(mesh2.boundingSphere.radius) ? mesh2.transformationState.position : mesh2.boundingSphere.center;

        GLKMatrix4 mesh1ToCameraSpace = [mesh1 nodeToOtherNodeTransform:self];
        GLKMatrix4 mesh2ToCameraSpace = [mesh2 nodeToOtherNodeTransform:self];

        GLKVector3 depth1InCameraSpace = GLKMatrix4MultiplyVector3WithTranslation(mesh1ToCameraSpace, depth1);
        GLKVector3 depth2InCameraSpace = GLKMatrix4MultiplyVector3WithTranslation(mesh2ToCameraSpace, depth2);


        NSNumber *n1 = [NSNumber numberWithFloat:depth1InCameraSpace.z];
        NSNumber *n2 = [NSNumber numberWithFloat:depth2InCameraSpace.z]; /* Breakpoint triggered here */

        if(sortedFromFrontToBack)
        {
            return [n2 compare:n1];
        }
        return [n1 compare:n2];
    }];

    return sortedMeshes;
}

As I commented, the [NSNumber numberWithFloat:] call throws the malloc error. This method gets called once each frame from my GLKViewController's drawInRect method. Essentially, I have a class which keeps track of my cameras and the meshes which are going to be drawn by OpenGL, and it sorts them in camera space from either front to back for opaque meshes or back to front for transparent before drawing them.

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClearColor(self.clearColor.r, self.clearColor.g, self.clearColor.b, self.clearColor.a);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    DSDirector *director = [DSDirector sharedDirector];

    for(DSMesh *mesh in director.opaqueMeshes)
    {
        [mesh draw];
    }

    /* The director class keeps track of my scene's cameras and meshes and calls the above method to return the scene's transparent meshes properly sorted */
    for(DSMesh *mesh in director.transparentMeshes) 
    {
        [mesh draw];
    }
}

From what I've read, the autorelease pool should drain at the end of each run loop, so I wouldn't think that creating a bunch of autoreleased objects every frame is an issue, they should all get flushed each frame. I've profiled my program and checked for any leaks and can't find any, and I'm using ARC as well, which should minimize the risk. When I profile it, the live bytes total never budges, although the overall bytes rises quite quickly, and no leaks are found. In addition, didReceiveMemoryWarning never fires. I'm stumped.


Solution

  • So, I think I've figured out what was going on. At some prior point I'd enabled NSZombies and then forgot about it, and so all of my objects that I expected to be deallocated were actually still hanging around. When profiling in Instruments, zombies must not be counted as live, which is why I couldn't figure out why I was seemingly running out of memory when the number of live bytes wasn't climbing. However, when I upgraded to XCode 5, the new memory gauge showed that I was quickly racking up memory, and then when I dropped into Instruments, Leaks was covered up by a warning that said that it would not work properly since NSZombies were enabled. So, I disabled zombies, and now my memory usage is staying constant, just like I expected it to.