The role of autoreleasepool in Swift and thoughts about memory management in Swift

When using the following code, much memory (1000 times the file size) is used by the program after the following loop with 1000 iterations:

for _ in 1...1000 {
    let _ = try Data(contentsOf: URL(fileURLWithPath: "myFile"))
}

print("\nPRESS RETURN TO CONTINUE"); let _ = readLine()

When using Data(contentsOf: URL(fileURLWithPath: "myFile")), options: [.alwaysMapped]) the file content does not add up to memory, but even using the code above should free the memory, and I am not sure that I would like to use the alwaysMapped option (any opinion on that?).

I know that in Objective C, some allocation annotation can be used, but this is Swift, I would think that memory should be freed when the object is not used any more.

Is this a bug in the Swift library or am I seeing it the wrong way?

UPDATE: Another case is the following, where with autoreleasepool (cf. the comments below) the program needs 25 MB, and without it it needs 180 MB:

for _ in 1...1000000 {
    autoreleasepool {
        s = s.replacingOccurrences(of: "[eo]", with: "l", options: .regularExpression, range: nil)
    }
}

Note that 1) String.replacingOccurrences(of:with:options:range:) does work with Foundation across platforms, and 2) autoreleasepool is not even mentioned in the Swift book.

UPDATE 2: I changed the title from "(Memory leaks with Data.init(contentsOf:) and String.replacingOccurrences(of:with:options:range:)?" to "The role of autoreleasepool in Swift", because this should be "a topic".

you can do it in swift as well:

for _ in 1...1000 {
    autoreleasepool { 
        let _ = try Data(contentsOf: URL(fileURLWithPath: "myFile"))
    }
}
1 Like

try autoreleasepool {...} seems to work in my case, but I find it strange to use this in a Swift program. Why should this be necessary? It is pure "Foundation" here.

that it is not even mentioned (but obviously required) looks like a documentation omission.

i concur, this is not obvious at all, especially for newcomers who never touched objective-c.

There's not much purity in "Foundation". On Apple platforms Foundation isn't really a swift library. It's mostly closed-source Objective-C code with other languages mixed in, written before Swift was a thing.

On linux and Windows there's swift-corelibs-foundation which is written in Swift, and a completely different codebase than the apple Foundation

So one should use swift-corelibs-foundation on Apple platforms too... I suppose no autoreleasepool there.

That would come with a lot of downsides. If you cannot use closed source Foundation, you cannot interoperate with Objective-C. Which means no UIKit/AppKit. Which means no SwiftUI.

On linux and Windows there's swift-corelibs-foundation which is written in Swift, and a completely different codebase than the apple Foundation

No, there is significant overlap, from the swift-corelibs-foundation design doc:
A significant portion of the implementation of Foundation on Apple platforms is provided by another framework called CoreFoundation (a.k.a. CF). CF is written primarily in C and is very portable. Therefore we have chosen to use it for the internal implementation of Swift Foundation where possible. As CF is present on all platforms, we can use it to provide a common implementation everywhere.

I agree that autoreleasepool is confusing for people like me who have never used Swift on Apple platforms, which is why Swift DocC requires shims like this.

2 Likes

Good, I'll take that. Shouldn't it be ... || os(Windows)?

Pull request welcome. :smiley:

And I'm sure it's true, for some definition of when the object is not used any more.
Swift doesn't have any guarantees for lifetimes of objects. Once your loop has exited I expect the memory is released, assuming your app hasn't crashed due to out of memory.
While autoreleasepool isn't commonly needed in swift using an unbounded or large amount of Foundation objects and using background threads are cases where it may be needed.

Swift has at least the promise „when nothing is referencing your object, it will be freed“. Interpreting this as „it will be freed at any later point“ is then really not enough. In non-concurrent code I expect the object to be freed the very moment the references to it are removed. From a technical standpoint, I would not understand why this should happen at a later moment.(*) In the cited loop, where some possibly large chunks of memory get allocated, I need to have the garantee that in the next iteration, where the old object has lost any references to it, the memory has been freed. I certainly do not want to have all those large chunks of memory accumulated, this would just not be OK. Else, I need my manual memory management back (at least at this place)(**). So if Swift does not have guarantees for freeing memory, maybe it needs them. Another place where Swift seems not to have garantees is the layout of arrays of structs, but I think many people expect a certain layout for the obvious cases. The problem with the point of view that a "higher" computer language should not be concerned with those implementation issues is that at some points, such a language then cannot be used (or you need to write "ugly" code).


(*) In Java you only have the garantee that things will be freed at some later point, but there you give an upper bound for the memory to be used by the program and the garbage collector kicks in at least when you are near that upper bound. In a situation where there is not such an upper bound (and I am happy that Swift programs do not need those upper bounds) I certainly do need other guarantees.

(**) I know you can do manual memory management in Swift (UnsafePointer etc.), but saying "OK, Swift is a nice language with this nice automatic reference counting, but at some (many?) places you then have to manage the memory manually" does not sound good to me. The promise "you can write nice code and it will be efficient" sounds much better.

1 Like

I think it's worth filing a bug report for it

OK, good idea. I actually added the following three bug reports (I found some related tickets for the first, but none for the others):

... I am not sure about at what point to issue a bug report besides discussion in the forum, is there some kind of guideline for this? Issues 2 and 3 would be something for Swift Evolution, but there you already need an implementation for it in order to be discussed, right?

1 Like

My understanding is that if something is a problem or if something can be done better, then it's worth a bug report. I think in general, Foundation things (like URL and String.replacingOccurrences(of:with:options:range:) here) are outside of Swift Evolution, so you don't need to go through the entire "discussion → pitch → implementation → proposal" process for them. But even for things that need to go through the evolution process, you can still discuss about them before having an implementation–they just can't be proposed and accepted without one.

1 Like
Terms of Service

Privacy Policy

Cookie Policy