Re: How to ease the burden on the Garbage Collector?
Re: How to ease the burden on the Garbage Collector?
- Subject: Re: How to ease the burden on the Garbage Collector?
- From: Gabriel Zachmann <email@hidden>
- Date: Fri, 9 Oct 2009 16:23:34 +0200
It seems that about 20% of the time is spent in the Garbage
Collector thread!
Which is a bit surprising to me, since I don't allocate a large
number of objects (I think) -- just a small number of large objects
(the images).
The collector only chews CPU when there are lots of allocation
events (and, hopefully, deallocation events) that are causing the
collector to believe it needs to do work. Or when something is
triggering the collector manually really often.
Have a look at ObjectAlloc & ObjectGraph in Instruments. That
should give you an idea if there are lots and lots of allocation
events occurring and, if so, what they are. From there, it is a
matter of minimizing the number of allocations occurring.
If I understand the output of Instruments/ObjectAlloc correctly, it
says there have been about 300,000 allocations withing about 1.5
minutes.
Is that very much?
It also says that most of the allocations are CFStrings and small
memory blocks (surprise surprise ;-) ), but looking at the instances
it seems to me that I don't have much control over those.
(For instance, about half of the CFString's occur in the library
CFNetwork in the functions initializeTLDMachine() and
MemoryCookies::inflateFromData().)
Since I couldn't find a way to export the statistics in ASCII, I have
made 3 screenshots, which you can see here:
http://zach.in.tu-clausthal.de/tmp/malloc1.png
http://zach.in.tu-clausthal.de/tmp/malloc2.png
http://zach.in.tu-clausthal.de/tmp/malloc3.png
For your information, here is an outline of my main loop (executed in
ScreenSaverView's -animateOneFrame):
get a new filename from a list of filenames
CGImageSourceRef sourceRef = CGImageSourceCreateWithURL(..)
check a few things in the sourceRef, such as image size
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0,
NULL)
[NSTimer scheduledTimerWithTimeInterval: 2.3 target: self selector:
@selector(doGarbageCollection:) userInfo: nil repeats: NO];
create new layer with contents = imageRef
create a new animation and attach it to the new layer
[CATransaction begin];
[CATransaction setValue: [NSNumber numberWithFloat:
fading_duration] forKey: kCATransactionAnimationDuration ];
[mainLayer_ replaceSublayer: currentLayer_ with: newlayer];
currentLayer_ = newlayer;
[CATransaction commit];
This gets executed every 5 seconds.
And here is what doGarbageCollection does:
- (void) doGarbageCollection: (NSTimer *) theTimer
{
if ( [NSGarbageCollector defaultCollector] )
[[NSGarbageCollector defaultCollector]
collectExhaustively]; // seems to work better than
collectIfNeeded
}
I have tried omitting doGarbageCollection: , but then the
screensaver's memory footprint grows until the memory is full, and
then the animation stops for a second or 2 (you can see the memroy
being freed in ActivityMonitor).
The judder I am concerned with seems to occur especially during image
loading.
Again, the same code, when executed in the reference-counted
environment (10.5) runs very smoothly, no judder whatsoever, not even
during loading of an image.
Are you sure you aren't just seeing the GC thread sitting and
waiting for something to do?
THat would mean that the GC thread does busy waiting, wouldn't it?
What are the "hot" methods/functions you see related to the GC
thread? (many folks misread what Shark is telling them...)
Here are the top lines of the call tree that I see in Shark.
It seems to me that they all belong to the same thread, the GC thread.
So, one of the hot methods is
Auto::MemoryScanner::scan_for_unmarked_blocks, isn't it?
0.0% 31.7% libSystem.B.dylib start_wqthread
0.0% 31.7% libSystem.B.dylib _pthread_wqthread
0.0% 31.7% libSystem.B.dylib _dispatch_worker_thread2
0.0% 20.4% libSystem.B.dylib _dispatch_queue_invoke
0.0% 20.4% libSystem.B.dylib _dispatch_queue_drain
0.0% 20.4% libSystem.B.dylib _dispatch_call_block_and_release
0.0% 20.4% libauto.dylib auto_collection_work(Auto::Zone*)
0.0% 20.4% libauto.dylib auto_collect_internal(Auto::Zone*,
unsigned int)
0.0% 20.3% libauto.dylib Auto::Zone::collect(bool, void*,
unsigned long long*)
0.0% 18.5% libauto.dylib Auto::MemoryScanner::scan()
0.6% 17.1% libauto.dylib
Auto::MemoryScanner::scan_pending_until_done()
0.6% 15.0% libauto.dylib
Auto::MemoryScanner::scan_for_unmarked_blocks(Auto::Subzone*, unsigned
long, void*)
0.4% 7.3% libauto.dylib
Auto::MemoryScanner::scan_object_range(Auto::Range&,
Auto::WriteBarrier*)
3.2% 4.7% libauto.dylib
Auto::MemoryScanner::scan_range(Auto::Range const&, Auto::WriteBarrier*)
1.4% 1.4% libauto.dylib
Auto::MemoryScanner::set_pending(void*)
0.1% 0.1% libauto.dylib
Auto::MemoryScanner::check_block(void**, void*)
2.0% 2.0% libobjc.A.dylib objc_layout_for_address
0.1% 0.1% libobjc.A.dylib class_getIvarLayout
0.0% 0.0% libauto.dylib
Auto::MemoryScanner::set_pending(void*)
0.0% 0.0% libauto.dylib
Auto::MemoryScanner::check_block(void**, void*)
5.2% 7.0% libauto.dylib
Auto::MemoryScanner::scan_range(Auto::Range const&, Auto::WriteBarrier*)
0.1% 0.1% libauto.dylib
Auto::MemoryScanner::set_pending(void*)
0.0% 0.0% libauto.dylib
Auto::MemoryScanner::check_block(void**, void*)
0.0% 0.0% libobjc.A.dylib objc_layout_for_address
0.9% 1.4% libauto.dylib
Auto::MemoryScanner::scan_range(Auto::Range const&, Auto::WriteBarrier*)
0.0% 0.0% libauto.dylib
Auto::MemoryScanner::set_pending(void*)
0.0% 0.0% libauto.dylib
Auto::MemoryScanner::scan_object_range(Auto::Range&,
Auto::WriteBarrier*)
0.0% 1.4% libauto.dylib Auto::Collector::check_roots()
0.0% 0.0% libauto.dylib
Auto::MemoryScanner::scan_root_ranges()
0.0% 0.0% libauto.dylib
Auto::MemoryScanner::scan_thread_ranges(bool, bool)
0.0% 0.0% libauto.dylib Auto::Collector::scan_barrier()
1.8% 1.8% libauto.dylib bool
Auto::visitAllocatedBlocks<Auto::scavenge_blocks_visitor>(Auto::Zone*,
Auto::scavenge_blocks_visitor&)
0.0% 0.0% libauto.dylib weak_clear_references
0.0% 0.1% libauto.dylib
Auto::Zone::invalidate_garbage(unsigned long, unsigned long const*)
0.0% 0.0% libSystem.B.dylib vm_msync
0.0% 0.0% libauto.dylib Auto::Zone::free_garbage(unsigned
int, unsigned long, unsigned long*, unsigned long&, unsigned long&)
0.0% 0.0% libauto.dylib Auto::Admin::purge_free_space()
0.0% 11.2% libSystem.B.dylib _dispatch_apply2
0.0% 11.2% RawCamera GetRawPluginsInfo
0.0% 8.2% RawCamera RCCopyMethodsArrayForIdentifier
0.0% 3.0% RawCamera GetRawPluginsInfo
0.0% 2.9% libSystem.B.dylib dispatch_apply_f
0.0% 2.9% libSystem.B.dylib _dispatch_apply2
0.0% 2.9% RawCamera GetRawPluginsInfo
0.0% 2.9% RawCamera RCCopyMethodsArrayForIdentifier
1.7% 2.9% RawCamera GetRawPluginsInfo
0.0% 0.0% RawCamera GetRawPluginsInfo
0.0% 0.0% libSystem.B.dylib calloc
0.0% 0.0% RawCamera RCCopyMethodsArrayForIdentifier
There is another thread, NSApplicationMain, that takes 25% CPU, of
which almost everything is spent in CA::Render::copy_image (which
seems plausible to me).
Anf then there is one thread that takes about 20% CPU, and which
spends almost all time in CA::OGL::Context::render.
Any ideas, suggestions, and insights will be highly appreciated.
Best regards,
Gabriel.
Attachment:
smime.p7s
Description: S/MIME cryptographic signature
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden