> On Jul 14, 2017, at 2:35 PM, John McCall <rjmcc...@apple.com> wrote:
> 
>> On Jul 14, 2017, at 1:12 PM, Charles Srstka via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> MOTIVATION:
>> 
>> Meet Bob. Bob is a developer with mostly C++ and Java experience, but who 
>> has been learning Swift. Bob needs to write an app to parse some proprietary 
>> binary data format that his company requires. Bob’s written this app, and 
>> it’s worked pretty well on Linux:
>> 
>> import Foundation
>> 
>> do {
>>     let url = ...
>>     
>>     let handle = try FileHandle(forReadingFrom: url)
>>     let bufsize = 1024 * 1024 // read 1 MiB at a time
>>     
>>     while true {
>>         let data = handle.readData(ofLength: bufsize)
>>         
>>         if data.isEmpty {
>>             break
>>         }
>>         
>>         data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) in
>>             // do something with bytes
>>         }
>>     }
>> } catch {
>>     print("Error occurred: \(error.localizedDescription)")
>> }
>> 
>> Later, Bob needs to port this same app to macOS. All seems to work well, 
>> until Bob tries opening a large file of many gigabytes in size. Suddenly, 
>> the simple act of running the app causes Bob’s Mac to completely lock up, 
>> beachball, and finally pop up with the dreaded “This computer is out of 
>> system memory” message. If Bob’s particularly unlucky, things will locked up 
>> tight enough that he can’t even recover from there, and may have to 
>> hard-reboot the machine.
>> 
>> What happened?
>> 
>> Experienced Objective-C developers will spot the problem right away; the 
>> Foundation APIs that Bob used generated autoreleased objects, which would 
>> never be released until Bob’s loop finished. However, Bob’s never programmed 
>> in Objective-C, and to him, this behavior is completely undecipherable.
>> 
>> After a copious amount of time spent Googling for answers and asking for 
>> help on various mailing lists and message boards, Bob finally gets the 
>> recommendation from someone to try wrapping the file handle read in an 
>> autorelease pool. So he does:
>> 
>> import Foundation
>> 
>> do {
>>     let url = ...
>>     
>>     let handle = try FileHandle(forReadingFrom: url)
>>     let bufsize = 1024 * 1024 // read 1 MiB at a time
>>     
>>     while true {
>>         let data = autoreleasepool { handle.readData(ofLength: bufsize) }
>>         
>>         if data.isEmpty {
>>             break
>>         }
>>         
>>         data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) in
>>             // do something with bytes
>>         }
>>     }
>> } catch {
>>     print("Error occurred: \(error.localizedDescription)")
>> }
>> 
>> Unfortunately, Bob’s program still eats RAM like Homer Simpson in an 
>> all-you-can-eat buffet. Turns out the data.withUnsafeBytes call *also* 
>> causes the data to be autoreleased.
> 
> This seems like a bug that should be fixed.  I don't know why the other one 
> would cause an unreclaimable autorelease.

Sticking a break at the end of my original code snippet so the loop runs only 
once and then running it through Instruments, it seems the NSConcreteData 
instance gets autoreleased… 32,769 times. o_O

First autorelease occurs inside -[NSConcreteFileHandle readDataOfLength:], the 
next 32,768 occur in Data.Iterator.next(), which is called by specialized 
RangeReplaceableCollection.init<A>.

I can send you the trace off-list if you’d like.

Charles

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to