Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-5463] Unnecessary autoreleases when working with FileHandle and Data #3832

Open
CharlesJS opened this issue Jul 14, 2017 · 5 comments
Open

Comments

@CharlesJS
Copy link

CharlesJS commented Jul 14, 2017

Previous ID SR-5463
Radar None
Original Reporter @CharlesJS
Type Bug

Attachment: Download

Environment

Xcode 9 beta 3

Additional Detail from JIRA
Votes 1
Component/s Foundation
Labels Bug
Assignee None
Priority Medium

md5: 099e1e81fcb28efa04895a4713447b19

Issue Description:

FileHandle's readData(ofLength:), as well as Data's withUnsafeBytes(), generate autoreleases on systems that feature the Objective-C bridge. If these are used in a loop to read a very large file, this can quickly consume all of the system's memory and render the machine unresponsive, for reasons that are not clear to a new developer unfamiliar with Objective-C and its pitfalls. It's also annoying to work around since an @autoreleasepool attribute can't simply be tacked onto a loop; instead, you must individually wrap every call that creates or accesses the data, or else flow control constructs such as 'break' and 'continue' become unavailable.

See this thread for more details:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170710/038085.html

I have attached three test cases:

"AutoreleaseFool" - reads a megabyte at a time from /dev/random using FileHandle. You can watch the program's memory usage creep upward as it runs.

"Otto von Releasepool" - This time the creation of the data is wrapped in an autorelease pool, but the data is then autoreleased while accessing it.

"No P in Our AutoreleaseOol" - This time both creation and access are wrapped in autorelease pools, and the program's memory usage finally behaves as one would expect.

@phausler
Copy link
Member

phausler commented Jul 15, 2017

I am not certain we can change the behavior of the objective-c classes since it would mean a runtime behavior difference. Does this particularly express here because the ARC optimization of autorelease return values is not happening? Or is it the lifespan caused by an autorelease caused by swift?

It definitely needs more detailed traces to isolate the offenders.

@phausler
Copy link
Member

phausler commented Jul 15, 2017

Also: what other cases are causing this failure? I doubt it is just FileHandle.

@CharlesJS
Copy link
Author

CharlesJS commented Jul 15, 2017

I assumed it was coming from somewhere inside the MRC code of the frameworks, which is why I pitched adding autoreleasepool wrappers to the overlay. I was asked to file a bug report instead, and so here we are.

Looking at these traces, we might be getting a little of both:

(lldb) bt
* thread #​1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.27
    frame #​0: 0x00007fffa7664a50 libobjc.A.dylib`-[NSObject autorelease]
  * frame #​1: 0x000000010000202b AutoreleaseFool`@nonobjc FileHandle.__allocating_init(forReadingFrom:) at main.swift:0
    frame #​2: 0x0000000100001a5f AutoreleaseFool`main at main.swift:14
    frame #​3: 0x00007fffa7f53235 libdyld.dylib`start + 1
(lldb) bt
* thread #​1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.27
  * frame #​0: 0x00007fffa7664a50 libobjc.A.dylib`-[NSObject autorelease]
    frame #​1: 0x00007fff9425f829 Foundation`-[NSConcreteFileHandle readDataOfLength:] + 639
    frame #​2: 0x0000000100001ae8 AutoreleaseFool`main at main.swift:18
    frame #​3: 0x00007fffa7f53235 libdyld.dylib`start + 1
(lldb) bt
* thread #​1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #​0: 0x00007fffa7665d70 libobjc.A.dylib`objc_autorelease
  * frame #&#8203;1: 0x00000001000587ed AutoreleaseFool`Foundation.Data.withUnsafeBytes<A, B>((Swift.UnsafePointer<B>) throws -> A) throws -> A + 141
    frame #&#8203;2: 0x0000000100001bd0 AutoreleaseFool`main at main.swift:22
    frame #&#8203;3: 0x00007fffa7f53235 libdyld.dylib`start + 1

@CharlesJS
Copy link
Author

CharlesJS commented Jul 15, 2017

I'm sure that there are many Cocoa classes that do this; however, FileHandle and Data were voted by their respective high school classes as most likely to chew through gigabytes of data in a loop, so I figured they'd be good starting points.

@rjmccall
Copy link
Member

rjmccall commented Jul 18, 2017

The access autoreleases are almost certainly tied to the use of NS_RETURNS_INNER_POINTER in NSData, not some general thing relating to the use of ObjC classes.

We are likely already tracking the creation autorelease as rdar://27528234, but it never hurts to have extra test cases.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@shahmishal shahmishal transferred this issue from apple/swift May 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants