I've created a fresh React Native project (v0.60.4) and I'm running it on a real iPhone 11 Pro iOS 13.1.3 in release mode. The app.js is super simple:
import React from 'react';
import {
SafeAreaView,
Image,
} from 'react-native';
const App: () => React$Node = () => {
return (
<SafeAreaView>
<Image source={require('./test.png')} />
</SafeAreaView>
);
};
export default App;
I've noticed that every time my app enters the background (when I switch to another app), I see a jump in memory usage that's never freed (even with memory warnings).
I can see that the increase is caused by a new ImageIO_PNG_Data, created as the app moves into the background.
While this image is relatively modest in size, in my real app it's causing app crashes after the user app switches a lot.
Here's the same app, same image, written in Swift, using the same method to create a UIImage that RN is using.
No extra images loaded into memory. What is RN doing differently that's loading these extra images into memory and not clearing the old ones? What's happening when the app enters the background?
Update
After marking a generation I noticed that all of the large images were coming from what looks like iOS's snapshot of the app as it entered the background:
0 libsystem_kernel.dylib mmap
1 ImageIO _ImageIO_Malloc
2 ImageIO AppleJPEGReadPlugin::copyImageBlockSet(InfoRec*, CGImageProvider*, CGRect, CGSize, __CFDictionary const*)
3 ImageIO IIO_Reader::CopyImageBlockSetProc(void*, CGImageProvider*, CGRect, CGSize, __CFDictionary const*)
4 ImageIO IIOImageProviderInfo::CopyImageBlockSetWithOptions(void*, CGImageProvider*, CGRect, CGSize, __CFDictionary const*)
5 CoreGraphics CGImageProviderCopyImageBlockSetWithOptions
6 QuartzCore CA::Render::copy_image(CGImage*, CGColorSpace*, unsigned int, double, double)
7 QuartzCore CA::Render::prepare_image(CGImage*, CGColorSpace*, unsigned int, double)
8 QuartzCore CA::Layer::prepare_commit(CA::Transaction*)
9 QuartzCore CA::Context::commit_transaction(CA::Transaction*, double)
10 QuartzCore CA::Transaction::commit()
11 UIKitCore __83-[UIApplication _createSnapshotContextForScene:withName:performLayoutWithSettings:]_block_invoke_4
12 UIKitCore -[UIApplication _performWithUICACommitStateSnapshotting:]
13 UIKitCore __83-[UIApplication _createSnapshotContextForScene:withName:performLayoutWithSettings:]_block_invoke_2
14 UIKitCore +[UIView(Animation) performWithoutAnimation:]
15 UIKitCore __83-[UIApplication _createSnapshotContextForScene:withName:performLayoutWithSettings:]_block_invoke
16 UIKitCore -[UIScene _applyOverrideSettings:forActions:]
17 UIKitCore -[UIWindowScene _applySnapshotSettings:forActions:]
18 UIKitCore -[UIApplication _createSnapshotContextForScene:withName:performLayoutWithSettings:]
19 UIKitCore __65-[UIApplication _performSnapshotsWithAction:forScene:completion:]_block_invoke_3
20 FrontBoardServices -[FBSSceneSnapshotAction _executeNextRequest]
21 FrontBoardServices -[FBSSceneSnapshotAction _executeNextRequest]
22 FrontBoardServices -[FBSSceneSnapshotAction executeRequestsWithHandler:completionHandler:expirationHandler:]
23 UIKitCore __65-[UIApplication _performSnapshotsWithAction:forScene:completion:]_block_invoke_2
24 UIKitCore -[UIApplication _beginSnapshotSessionForScene:withSnapshotBlock:]
25 UIKitCore __65-[UIApplication _performSnapshotsWithAction:forScene:completion:]_block_invoke
26 UIKitCore -[UIScene _enableOverrideSettingsForActions:]
27 UIKitCore -[UIScene _performSystemSnapshotWithActions:]
28 UIKitCore -[UIApplication _performSnapshotsWithAction:forScene:completion:]
29 UIKitCore __98-[_UISceneSnapshotBSActionsHandler _respondToActions:forFBSScene:inUIScene:fromTransitionContext:]_block_invoke_3
30 UIKitCore __98-[_UISceneSnapshotBSActionsHandler _respondToActions:forFBSScene:inUIScene:fromTransitionContext:]_block_invoke.30
31 UIKitCore -[UIApplication prepareSnapshotsWithAction:forScene:completion:]
32 UIKitCore __98-[_UISceneSnapshotBSActionsHandler _respondToActions:forFBSScene:inUIScene:fromTransitionContext:]_block_invoke_2
33 UIKitCore -[UIScene _emitSceneSettingsUpdateResponseForCompletion:afterSceneUpdateWork:]
34 UIKitCore -[UIScene scene:didUpdateWithDiff:transitionContext:completion:]
35 UIKitCore -[UIApplicationSceneClientAgent scene:handleEvent:withCompletion:]
36 FrontBoardServices -[FBSSceneImpl updater:didUpdateSettings:withDiff:transitionContext:completion:]
37 FrontBoardServices __88-[FBSWorkspaceScenesClient sceneID:updateWithSettingsDiff:transitionContext:completion:]_block_invoke_2
38 FrontBoardServices -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:]
39 FrontBoardServices __88-[FBSWorkspaceScenesClient sceneID:updateWithSettingsDiff:transitionContext:completion:]_block_invoke
40 libdispatch.dylib 0x102a9b2a7
41 libdispatch.dylib 0x102a9e9cf
42 FrontBoardServices __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__
43 FrontBoardServices -[FBSSerialQueue _queue_performNextIfPossible]
44 FrontBoardServices -[FBSSerialQueue _performNextFromRunLoopSource]
45 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
46 CoreFoundation __CFRunLoopDoSource0
47 CoreFoundation __CFRunLoopDoSources0
48 CoreFoundation __CFRunLoopRun
49 CoreFoundation CFRunLoopRunSpecific
50 GraphicsServices GSEventRunModal
51 UIKitCore UIApplicationMain
52 tmp main /...
53 libdyld.dylib start