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-9512] NSBitmapImageRep.representation(using: .png, properties: [:]) causing memory leak on CGImage #3570

Closed
swift-ci opened this issue Dec 14, 2018 · 4 comments

Comments

@swift-ci
Copy link
Contributor

Previous ID SR-9512
Radar None
Original Reporter dastrobu (JIRA User)
Type Bug
Status Resolved
Resolution Invalid
Environment

Apple Swift version 4.2.1 (swiftlang-1000.11.42 clang-1000.11.45.1)
Target: x86_64-apple-darwin18.2.0

Additional Detail from JIRA
Votes 0
Component/s Compiler, Foundation
Labels Bug, Leak
Assignee None
Priority Medium

md5: fa24e326d689d53529a7d02d27619a19

Issue Description:

Reference counting seems not to be working correctly on CGImage when an png representation is created.

The following minimal test case illustrates the problem:

func testDeallocCgImageWhenPngRepresentationWasCreated() {
    weak var weakCgImage: CGImage?
    do {
        let data: [UInt8] = [0, 0, 0, 0]
        let provider: CGDataProvider = CGDataProvider(data: Data(
            bytes: UnsafeMutableRawPointer(mutating: data),
            count: 4
        ) as CFData)!

        let cgImage = CGImage(
            width: 1,
            height: 1,
            bitsPerComponent: 1 * 8,
            bitsPerPixel: 4 * 8,
            bytesPerRow: 4 * 1,
            space: CGColorSpaceCreateDeviceRGB(),
            bitmapInfo: CGBitmapInfo(),
            provider: provider,
            decode: nil,
            shouldInterpolate: false,
            intent: CGColorRenderingIntent.defaultIntent
        )!
        weakCgImage = cgImage

        let imageRep: NSBitmapImageRep = NSBitmapImageRep(cgImage: cgImage)
        imageRep.size = CGSize(width: CGFloat(1), height: CGFloat(1))

        // the following line causes the memory leak (if removed the test is green)
        _ = imageRep.representation(using: .png, properties: [:])
        XCTAssertNotNil(weakCgImage)
    }
    // test if cg image was deallocated
    XCTAssertNil(weakCgImage)
} 

The problematic call seems to be

_ = imageRep.representation(using: .png, properties: [:]) 

I originally found this problem in one of my apps, which was creating millions of image representations. The memory leak on CGImage caused the memory footprint to grow until the process got killed by the kernel.

Apart from a fix to the problem, any recommendations on a memory leak free workaround for the conversion of a CGImage to an in memory Data png representation are welcome.

@swift-ci
Copy link
Contributor Author

Comment by Daniel Strobusch (JIRA)

If I should create a pull request with the failing test, please let me know.

@belkadan
Copy link

What happens if you put an autorelease pool around the last call?

@swift-ci
Copy link
Contributor Author

Comment by Daniel Strobusch (JIRA)

Great hint, I wasn't aware of the autoreleasepool option. So the following change lets the test pass.

autoreleasepool {
    _ = imageRep.representation(using: .png, properties: [:])
} 

@belkadan thanks for providing a workaround.

@belkadan
Copy link

I hate to say it, but that's not a workaround; it's a necessary part of working with frameworks implemented in Objective-C. Autorelease pools are a way to delay the destruction of an object, usually for returning values that a caller isn't expecting to have to release themselves. By default, there's a fresh autorelease pool every time an event is handled, but if you've got a loop that's doing a lot of work you may need to add your own like this.

@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
This issue was closed.
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

2 participants