How to avoid/evade garbage-collection bug resulting in NSClipViewBackingLayer being over-retained
Problem:
Suspected crash in garbage collector.
Question:
How do you deal with this?
Is there anything else I can do other than filing a bug report? An obvious solution would be: "do not resize the view", but that would hurt functionality too much. Would it be useful to prevent the views from being redrawn until resizing is done? I tried [… removeFromSuperView], then resize, then [… addSubView:…]; That doesn't help.
How can I avoid/evade this problem?
The error message:
malloc: garbage block 0x2008a71a0(_NSClipViewBackingLayer[128]) was over-retained during finalization, refcount = 1
This could be an unbalanced CFRetain(), or CFRetain() balanced with -release.
Break on auto_zone_resurrection_error() to debug.
malloc: fatal resurrection error for garbage block 0x2008a71a0(_NSClipViewBackingLayer[128]): over-retained during finalization, refcount = 1
The thread that is crashing is a background thread from the garbage collector called "Garbage Collection Work Queue".
When I break on auto_zone_resurrection_error and do a stack backtrace, I get:
0 0x00007fff8094aba4 in auto_zone_resurrection_error ()
1 0x00007fff80959ce6 in Auto::Zone::handle_overretained_garbage ()
2 0x00007fff8095a0b4 in Auto::Zone::free_garbage ()
3 0x00007fff80944bd3 in auto_collect_internal ()
4 0x00007fff809450cd in auto_collection_work ()
5 0x00007fff80626284 in _dispatch_call_block_and_release ()
6 0x00007fff80604df2 in _dispatch_queue_drain ()
7 0x00007fff80604c54 in _dispatch_queue_invoke ()
8 0x00007fff806047fe in _dispatch_worker_thread2 ()
9 0x00007fff80604128 in _pthread_wqthread ()
10 0x00007fff80603fc5 in start_wqthread ()
I suspect the problem is related to the IKImageViews that are being resized. Why do I suspect this?
Taming IKImageView has been a royal PITA from start to finish (under-documented, buggy, lots of features but no access to them).
The crash occurs during resizing of a NSScrollView with 3 IKImageViews.
The Main thread is halted at [IKImageView zoomImageToFit:]
Update in response to @bbum and @Peter Hosey
I was able to not fix, but mostly circumvent the crashing garbage collector.
- Enabled: MallocStackLoggingNoCompact AUTO_REFERENCE_COUNT_LOGGING
- reproduced crash
- break on auto_zone_resurrection_error
- get address of over-retained block (0x20242cde0 in this case, just for reference to the below)
info malloc 0x20242cde0 then produced a lot of output on this address (290 Kb of plain text). The most frequent mention was: NSView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView
As I expected something seemed to be happening during drawing of an NSView.
I subsequently tried Instruments as suggested by @Peter. This produces a rather long history of allocated objects. I needed to look at the Crash Report to get the address of the over-retained block (while running Instruments there is no output to the console and Instruments doesn't immediately indicate which is the offending block (or I don't know where to look, which is entirely possible)).
# Object Address Category Creation Time Live Size Responsible Library Responsible Caller
659531 0x20242cde0 CFRunLoopObserver 00:21.577.876 128 AppKit -[NSApplication setWindowsNeedUpdate:]
659532 0x20242cde0 Malloc 128 Bytes 00:26.352.081 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659533 0x20242cde0 Malloc 128 Bytes 00:26.352.328 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659534 0x20242cde0 Malloc 128 Bytes 00:26.351.892 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659535 0x20242cde0 Malloc 128 Bytes 00:26.321.389 128 QuartzCore CA::Transaction::add_root(CALayer*)
659536 0x20242cde0 Malloc 128 Bytes 00:26.321.380 • 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659537 0x20242cde0 Malloc 128 Bytes 00:26.351.710 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659538 0x20242cde0 Malloc 128 Bytes 00:26.320.100 128 QuartzCore CA::Transaction::add_root(CALayer*)
659539 0x20242cde0 Malloc 128 Bytes 00:26.351.433 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659540 0x20242cde0 _NSClipViewBackingLayer 00:26.293.575 • 128 AppKit -[NSClipView makeBackingLayer]
659541 0x20242cde0 Malloc 128 Bytes 00:26.293.601 • 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659542 0x20242cde0 Malloc 128 Bytes 00:26.351.311 • 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659543 0x20242cde0 Malloc 128 Bytes 00:26.294.761 • 128 QuartzCore CAViewSetLayer
659544 0x20242cde0 Malloc 128 Bytes 00:26.294.764 • 128 QuartzCore CA::Context::set_layer(CALayer*)
659545 0x20242cde0 Malloc 128 Bytes 00:26.294.767 • 128 QuartzCore CA::Transaction::add_root(CALayer*)
659546 0x20242cde0 Malloc 128 Bytes 00:26.320.091 • 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659547 0x20242cde0 Malloc 128 Bytes 00:26.347.524 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659548 0x20242cde0 NSConcreteNotification 00:20.730.342 64 Foundation +[NSConcreteNotification newTempNotificationWithName:object:userInfo:]
659549 0x20242cde0 CFNumber 00:16.433.727 32 Foundation -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:]
659550 0x20242cde0 NSRectSet 00:20.750.542 64 AppKit -[NSView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:]
659551 0x20242cde0 Malloc 128 Bytes 00:21.189.518 128 Foundation -[NSMutableIndexSet _ensureRangeCapacity:]
659552 0x20242cde0 Malloc 128 Bytes 00:26.322.017 • 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659553 0x20242cde0 Malloc 128 Bytes 00:26.356.013 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659554 0x20242cde0 Malloc 128 Bytes 00:26.356.440 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659555 0x20242cde0 Malloc 128 Bytes 00:26.404.886 • 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659556 0x20242cde0 Malloc 128 Bytes 00:26.404.887 128 QuartzCore CA::Transaction::add_root(CALayer*)
659557 0x20242cde0 Malloc 128 Bytes 00:26.356.824 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659558 0x20242cde0 Malloc 128 Bytes 00:26.299.972 128 QuartzCore CALayerCollectAllLayers(X::List<CALayer*>*, CALayer*, CA::Transaction*, x_heap_struct*)
659559 0x20242cde0 Malloc 128 Bytes 00:26.322.025 128 QuartzCore CA::Transaction::add_root(CALayer*)
659560 0x20242cde0 CFString (store) 00:23.702.683 128 CoreUI CUIRenderer::CreateImage(long, CUIDescriptor const*, CGRect*, double*, unsigned short*, unsigned char*)
659561 0x20242cde0 Malloc 128 Bytes 00:26.334.571 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659562 0x20242cde0 Malloc 128 Bytes 00:26.332.018 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659563 0x20242cde0 Malloc 128 Bytes 00:26.357.443 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659564 0x20242cde0 Malloc 128 Bytes 00:26.322.144 • 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659565 0x20242cde0 Malloc 128 Bytes 00:26.322.150 128 QuartzCore CA::Transaction::add_root(CALayer*)
659566 0x20242cde0 Malloc 128 Bytes 00:26.315.651 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659567 0x20242cde0 Malloc 128 Bytes 00:26.333.920 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659568 0x20242cde0 CFString 00:15.204.217 32 Foundation -[NSPlaceholderString initWithFormat:locale:arguments:]
659569 0x20242cde0 Malloc 128 Bytes 00:26.313.695 128 QuartzCore CALayerCollectLayers_(CALayer*, _CALayerIvars*, collect_layers_closure*)
659570 0x20242cde0 Malloc 128 Bytes 00:26.588.328 • 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659571 0x20242cde0 Malloc 128 Bytes 00:26.325.239 • 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659572 0x20242cde0 Malloc 128 Bytes 00:26.367.861 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659573 0x20242cde0 Malloc 128 Bytes 00:26.325.742 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659574 0x20242cde0 Malloc 128 Bytes 00:26.333.519 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659575 0x20242cde0 Malloc 128 Bytes 00:26.361.547 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659576 0x20242cde0 Malloc 128 Bytes 00:26.367.493 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659577 0x20242cde0 Malloc 128 Bytes 00:26.333.125 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659578 0x20242cde0 Malloc 128 Bytes 00:26.362.011 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659579 0x20242cde0 CFBasicHash 00:16.269.569 64 Foundation +[NSBundle preferredLocalizationsFromArray:forPreferences:]
659580 0x20242cde0 Malloc 128 Bytes 00:26.362.442 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659581 0x20242cde0 Malloc 128 Bytes 00:22.918.104 128 libauto.dylib Auto::foreach_block_do(auto_zone_cursor*, void (*)(void*, void*), void*)
659582 0x20242cde0 Malloc 128 Bytes 00:26.326.245 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659583 0x20242cde0 Malloc 128 Bytes 00:26.363.024 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659584 0x20242cde0 Malloc 128 Bytes 00:26.326.401 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659585 0x20242cde0 CFString (store) 00:16.808.912 128 Foundation -[NSPlaceholderString initWithFormat:locale:arguments:]
659586 0x20242cde0 Malloc 128 Bytes 00:26.326.792 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659587 0x20242cde0 Malloc 128 Bytes 00:26.367.083 128 QuartzCore CALayerTransactionFlagsLocation_(CALayer*, CA::Transaction*, _CALayerIvars*)
659588 0x20242cde0 CFString (store) 00:22.935.991 128 CoreUI CUIRenderer::CreateImage(long, CUIDescriptor const*, CGRect*, double*, unsigned short*, unsigned char*)
659589 0x20242ce00 Malloc 128 Bytes 00:20.100.480 128 Foundation allocateCollectableUnscannedWeakStorageWithSentinel
The object was not being over-retained in my code, which made it difficult to exactly pinpoint what was going wrong where. However from the above it became clear that something was going wrong during Core Animation.
I had noticed that it was easiest to reproduce the crash when I repeatedly resized the window, i.e. make it larger and then somewhat more larger. I suspect that the crash is caused by the second resize occurring while the IKImageViews inside the NSScrollView are still animating their contents in response to the first resize.
Partial "solution":
I already was not LiveResizing but updating the scrollview after the window had been resized.
I now block resizing of the main window for 1 second after each windowDidEndLiveResize. This strongly reduces the frequency of occurrence of the crash, but sadly not completely eliminates it.
(The IKImageView animation duration is about 300 msecs, but blocking resize for too short a duration is ineffective. Presumable GC is active for some time after then animation finished).
Question: can I disable animation on IKImageView completely?
I tried the undocumented private method [someIKImageView setAnimates:NO], but that does nothing.Why does this not completely eliminate the crash?
I think because I can still trigger resize notifications even though I am (trying to) block window resizing (by temporarily hiding the resize glyph and fixing max/min size).Question: Is there a better 开发者_运维百科way to prevent a window from resizing?
(I read this blog: vgable.com/blog/2008/04/11/nswindow-setresizable)[PS 1: info gc-roots 0x20242cde0 would just hang (I waited 10 minutes; no output; prompt never returned).]
[PS 2: this was also useful (@bbum's site) using-malloc-to-debug-memory-misuse-in-cocoa]
First, file a bug. This most likely a bug in IKImageView. Yuck.
Next, turn on malloc stack logging (see man malloc
) and also set AUTO_REFERENCE_COUNT_LOGGING
to YES
, you can use malloc_history
(or info malloc
) in GDB to see exactly where the object was retained.
Sounds like a bug in IK and CoreAnimation; please file it. Namely, if there are outstanding animations occurring during resize, then -- effectively -- there are transactions in flight. When the collector sees that the object is garbage, CA coincidentally creates a new strong reference via CFRetain() while the object is being finalized.
In theory, you should be able to hold a strong reference -- CFRetain or stick it in a global array or something -- to keep the object from being collected. Then, once the object has been removed from the render tree and all animations are truly done, you could remove the object's strong reference and it'll go away without complaint [hopefully -- there may still be a bug that'll cause issue even with this].
And, really, please file a bug and point to this SO question in your bug. This should not be happening.
精彩评论