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

iOS Memory Leak when KTOR Get Request is Made #1420

Closed
Tony-T-R-A-N opened this issue Oct 31, 2019 · 2 comments
Closed

iOS Memory Leak when KTOR Get Request is Made #1420

Tony-T-R-A-N opened this issue Oct 31, 2019 · 2 comments
Assignees

Comments

@Tony-T-R-A-N
Copy link

Hello, I am having an issue with iOS memory leaks when I make a ktor get request.
The dispatcher is being built on the iosX64/iosArm32/iosArm32 side through an class implementing CoroutineDispatcher() with the override fun dispatch doing

dispatch_async(dispatch_get_main_queue()) {
    block.run()
}
class DummyClass() : CoroutineScope {
​
	override val coroutineContext: CoroutineContext
        get() = dispatcher
	
	fun execute(suspendFunc: suspend () -> Unit) {
		launch { suspendFunc() }
	}    
​
	fun request(
		parameter: String,
		callback: (MyResponse?, Throwable?) -> Unit
	) {
		execute {
			try {
				val result = httpClient.get<MyResponse> {
					url("PUT A URL HERE")
				}
​
				callback(result, null)
			} catch (e: Throwable) {
				callback(null, e)
			} finally {
				executor.cancel()
			}
		}
	}
	
}
@e5l e5l self-assigned this Nov 8, 2019
bherbst added a commit to bherbst/ktor that referenced this issue Dec 10, 2019
@bherbst
Copy link
Contributor

bherbst commented Dec 10, 2019

I spent some time over the last few days digging into this issue, and believe I have found the issue (and solved it).

Background

We made a sample app that simply makes 300 API calls in quick succession in order to (roughly) test Ktor's performance and memory profile.

As it stands today (with Ktor 1.3.0-beta-2), the memory profile looks something like this, with the peak of the graph being roughly 122MB:

ktor_leak_graph

Looking at what is leaking, it appears as though the biggest source is a large number of 96KB mallocs in SSLBuffer, which are tracked on the green graph above. Here's a breakdown of some of those allocations and a stack trace:

ktor_leaked_objects

ktor_leak_trace

After looking through the call traces for these allocations, it appears as though the entire coroutine used for making the request was leaked, and these objects just happened to be the biggest single object connected to that leak.

The fix

Ktor's iOS client engine currently opens an NSUrlSession using sessionWithConfiguration(), which carries this warning for the delegate parameter:

Important

The session object keeps a strong reference to the delegate until your app exits or explicitly invalidates the session. If you do not invalidate the session by calling the invalidateAndCancel or finishTasksAndInvalidate method, your app leaks memory until it exits.

After forking IosClientEngine and adding a call to finishTasksAndInvalidate() at the end, the leak appears to be fixed and the memory consumption graph looks like this (again, the 96KB SSLBuffer allocations are shown in the green graph):

ktor_ios_leak_fixed

These allocations appear to be properly collected now, and no longer leak.

PR #1480 contains this fix.

@e5l
Copy link
Member

e5l commented Feb 29, 2020

Fixed in master

@e5l e5l closed this as completed Feb 29, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants