malloc: nano zone abandoned due to inability to preallocate reserved vm space

Viewed 9503

Since iOS 14 my iOS app prints this error message to the console (As the first message).

AppName(5088,0x1064438c0) malloc: nano zone abandoned due to inability to preallocate reserved vm space.

I couldn't find any other similar issues when searching on Google and I don't have any idea what's causing this. I'm running on an iPhone XS Max, iOS 14.0.1, Swift 5.

3 Answers

After digging into Apple's libmalloc source code, I found the offending function nano_malloc. You can view the code at Apple's open source site here.

As you can see from the code, nano_malloc call nano_preallocate_band_vm to pre-allocate a certain amount of heap memory (for optimization purpose, I guess). If the kernel does not return an address at exactly NANOZONE_SIGNATURE (0x6 << 44), nano_preallocate_band_vm report failure ==> nano_init print out the message.

Unlike Linux community, Apple does not comment on their internal implementations, so I do not have any authoritative answer. But here is my hypothesis:

  1. Libmalloc preallocate memory for optimization purpose only ==> Failed to do so does not affect the result of any program.
  2. Address/Thread sanitizers allocate their data structures before libmalloc does ==> libmalloc can not acquire a memory chunk at the exact address as it expect ==> failed to pre-allocate.

Regardless, as the source code shows, Apple give you 2 ways to turn off this warnings:

  1. Undef NANO_PREALLOCATE_BAND_VM (only viable if you compile libmalloc from source)
  2. Set the environment variable MallocNanoZone to 0.

I did try the 2nd option and success.

FYI, It's not Xcode or Swift's specific problem. I compiled C/C++ code with Address Sanitizer clang -fsanitize=address -o main main.c, the program emit exactly the same warning and the same solution worked.

My tools' version for future reference:

$ clang --version
Apple clang version 13.0.0 (clang-1300.0.29.3)
Target: x86_64-apple-darwin21.1.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

$ sw_vers
ProductName:    macOS
ProductVersion: 12.0.1
BuildVersion:   21A559

Try going to: set active scheme (next to the play and stop button) -> edit scheme -> diagnostics, then turn on "Thread Sanitizer." With Thread Sanitizer on, the error will be caught and you will receive a warning that is more descriptive in the debugger. If the description is a Swift access race, then you should use a Thread Safe Barrier in your code.

I don't know how you built your code but in my case, all I did is add a do try catch in my code. Something from this:

static func fetchAlbumWithAsyncURLSession() async throws -> [Album] {
    guard let url = URL(string: "https://itunes.apple.com/search?term=taylor+swift&entity=album") else {
        throw AlbumsFetcherError.invalidURL
    }
    let (data, _) = try await URLSession.shared.data(from: url)
    let iTunesResult = try JSONDecoder().decode(ITunesResult.self, from: data)
    return iTunesResult.results
}

To something like this:

static func fetchAlbumWithAsyncURLSession() async throws -> [Album] {
    guard let url = URL(string: "https://itunes.apple.com/search?term=taylor+swift&entity=album") else {
        throw AlbumsFetcherError.invalidURL
    }
    do {
        let (data, _) = try await URLSession.shared.data(from: url)
        let iTunesResult = try JSONDecoder().decode(ITunesResult.self, from: data)
        return iTunesResult.results
    } catch {
        print("Request failed with error: \(error)")
        return []
    }
}

And then I got the error :D

Best regards!

Related