apollo-ios: App crashing because of high memory usage

Bug report

Hi there! As a follow up of another bug reported, @blizzd and I have gathered some info about the high memory usage when fetching the items.

Versions

  • apollo-ios SDK version: 0.38.3
  • Xcode version: 12.4
  • Swift version: Swift 5
  • Package manager: CocoaPods

Further details

Hi Apollo team!

I’m working on the integration of the Apollo client in our app, migrating from a networking layer that was based on “old fashion” POST requests. I’m facing some issues because the app is crashing because of the very high usage of the memory.

.Context.

Our app has to fetch from the server a few thousand elements. For several reasons, we had to split requests in bulks of 300. Thus, if the app needs to download 1500 items, it will create 5 separated requests that, afterwards, will be combined together with their output.

.Gist of the technical implementations.

It’s based on ReactiveSwift. Hopefully, even with little familiarity with it, this should be digestible by anyone.

let fetchingSignalProducers: Array<SignalProducer<[Item], NoError>> = ...

SignalProducer<[Item], RequestError>
                .combineLatest(fetchingSignalProducers)
                .map { arrayOfarrayOfItems in
                	...
                }

Each one of the fetchingSignalProducers performs a fetch operation, running on a dedicated queue. The code above is running on a background, dedicated queue.

.Investigation.

We have been digging for a few days, using the Debug navigator, the Debug Memory Graph and the instruments.

These screenshots are from a run of the app stopped at a quite low level of usage compared to those that get to ~2GB.

Screenshot 2021-02-16 at 14 51 49

Screenshot 2021-02-16 at 14 52 06

Screenshot 2021-02-16 at 14 51 31

Screenshot 2021-02-16 at 14 52 18

Screenshot 2021-02-16 at 15 44 16 Screenshot 2021-02-16 at 15 44 54

From our investigation, it seems like the issue is whit this Array of PossiblyDeferred that creates a reference cycle.

Which other info can we provide for helping to fix this behaviour?

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 1
  • Comments: 26 (12 by maintainers)

Most upvoted comments

Hi @designatednerd !

I hope to add some valuable insight in this. I may have found the reason why the memory does not get free. This also made me dive a bit deeper : RetainCycle-GraphQLSelection

Now, it looks like your Zip3Accumulator which is using GraphQLResultAccumulator GraphQLResultAccumulator -> func zip<Accumulator1: GraphQLResultAccumulator, Accumulator2: GraphQLResultAccumulator, Accumulator3: GraphQLResultAccumulator>(_ accumulator1: Accumulator1, _ accumulator2: Accumulator2, _ accumulator3: Accumulator3) -> Zip3Accumulator<Accumulator1, Accumulator2, Accumulator3> is using a protocol with an associatedtype PartialResult for it, which the function attempts to verify and accept results, but at the same time is allocating memory for each next result, while holding the previous result in memory. O( N x N ).

https://stackoverflow.com/questions/44765347/does-enum-retain-its-associated-object

P.S. Experimenting with SQLStorage instead of In-Memory Storage only delayed the above effect.

Thanks for that info @Elaz-viz! We’ll look into this!