react-native-skia
react-native-skia copied to clipboard
Crashing on Android with only a Canvas
Description
For some reason, the app is crashing on Android after opening a modal with Canvas multiple times. Each time the modal is opened the memory usage goes up but it doesn't go back down when it's closed.
This problem was also happening on iOS but it seems like it was fixed in 0.1.184 beta.
This problem didn't seem to exist in v0.1.176 for Android.
Version
0.1.182 and 0.1.185
Steps to reproduce
Basically, you just need to keep opening and closing the modal until the app crashes.
Snack, code example, screenshot, or link to a repository
https://github.com/ivensgustavo/rnskia-android-crash
Hi @gustavomts! Thanks for the report and the example. With the latest version I'm not able to make it crash. The memory seems to be released correctly released when the Modal is dismissed. I'm running the profiler on an Android device. The fix we had in 0.1.184 was primarily targeting a regression we had on iOS that was not happening on Android. Could you try to profile the behaviour you're seeing and / or debug to see where or why it is crashing?
Hi @chrfalch, sure. I'll try to get back to you before the end of the week.
@chrfalch I have some logs here
2023-04-24 11:08:13.908 28184-28184 Choreographer com.rnskiacrash I Skipped 55 frames! The application may be doing too much work on its main thread.
2023-04-24 11:08:13.910 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_LOW) received by JS VM, ignoring because it's non-severe
2023-04-24 11:08:16.105 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_LOW) received by JS VM, ignoring because it's non-severe
2023-04-24 11:08:17.308 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_LOW) received by JS VM, ignoring because it's non-severe
2023-04-24 11:08:20.556 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_CRITICAL) received by JS VM, running a GC
2023-04-24 11:08:22.428 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_CRITICAL) received by JS VM, running a GC
2023-04-24 11:08:24.534 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_CRITICAL) received by JS VM, running a GC
2023-04-24 11:08:26.486 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_CRITICAL) received by JS VM, running a GC
2023-04-24 11:08:27.918 28184-28184 Choreographer com.rnskiacrash I Skipped 54 frames! The application may be doing too much work on its main thread.
2023-04-24 11:08:27.921 28184-8145 OpenGLRenderer com.rnskiacrash I Davey! duration=992ms; Flags=0, IntendedVsync=25991502724076, Vsync=25991569390740, OldestInputEvent=9223372036854775807, NewestInputEvent=0, HandleInputStart=25991581687105, AnimationStart=25991581689345, PerformTraversalsStart=25992490084071, DrawStart=25992490230789, SyncQueued=25992490700321, SyncStart=25992491538602, IssueDrawCommandsStart=25992492231779, SwapBuffers=25992494698602, FrameCompleted=25992496200165, DequeueBufferDuration=379531, QueueBufferDuration=728698, GpuCompleted=25958811601184,
2023-04-24 11:08:28.401 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_LOW) received by JS VM, ignoring because it's non-severe
2023-04-24 11:08:28.566 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_CRITICAL) received by JS VM, running a GC
2023-04-24 11:08:29.713 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_CRITICAL) received by JS VM, running a GC
2023-04-24 11:08:30.647 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_CRITICAL) received by JS VM, running a GC
2023-04-24 11:08:33.016 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_CRITICAL) received by JS VM, running a GC
2023-04-24 11:08:33.603 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_LOW) received by JS VM, ignoring because it's non-severe
2023-04-24 11:08:33.829 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_CRITICAL) received by JS VM, running a GC
2023-04-24 11:08:34.765 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_LOW) received by JS VM, ignoring because it's non-severe
2023-04-24 11:08:38.816 28184-28184 unknown:ReactModalHost com.rnskiacrash E Creating new dialog from context: com.rnskiacrash.MainActivity@e589d42@240688450
2023-04-24 11:08:39.527 28184-8145 OpenGLRenderer com.rnskiacrash I Davey! duration=715ms; Flags=1, IntendedVsync=26003386098672, Vsync=26003386098672, OldestInputEvent=9223372036854775807, NewestInputEvent=0, HandleInputStart=26003386509687, AnimationStart=26003386511042, PerformTraversalsStart=26003881847080, DrawStart=26004041132669, SyncQueued=26004092232310, SyncStart=26004092662726, IssueDrawCommandsStart=26004092768872, SwapBuffers=26004101621738, FrameCompleted=26004102203821, DequeueBufferDuration=103125, QueueBufferDuration=306198, GpuCompleted=0,
2023-04-24 11:08:39.528 28184-28184 unknown:ReactModalHost com.rnskiacrash E Updating existing dialog with context: com.rnskiacrash.MainActivity@e589d42@240688450
2023-04-24 11:08:39.528 28184-28184 Choreographer com.rnskiacrash I Skipped 42 frames! The application may be doing too much work on its main thread.
2023-04-24 11:08:40.480 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_LOW) received by JS VM, ignoring because it's non-severe
2023-04-24 11:08:42.603 28184-28184 Choreographer com.rnskiacrash I Skipped 35 frames! The application may be doing too much work on its main thread.
2023-04-24 11:08:43.943 28184-28184 Choreographer com.rnskiacrash I Skipped 78 frames! The application may be doing too much work on its main thread.
2023-04-24 11:08:44.015 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_LOW) received by JS VM, ignoring because it's non-severe
2023-04-24 11:08:44.015 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_CRITICAL) received by JS VM, running a GC
2023-04-24 11:08:45.207 28184-28184 unknown:ReactModalHost com.rnskiacrash E Creating new dialog from context: com.rnskiacrash.MainActivity@e589d42@240688450
2023-04-24 11:08:45.692 28184-28184 Choreographer com.rnskiacrash I Skipped 36 frames! The application may be doing too much work on its main thread.
2023-04-24 11:08:45.803 28184-28184 unknown:ReactModalHost com.rnskiacrash E Updating existing dialog with context: com.rnskiacrash.MainActivity@e589d42@240688450
2023-04-24 11:08:47.226 28184-8145 OpenGLRenderer com.rnskiacrash D endAllActiveAnimators on 0xb4000076b13b7300 (LayerDrawable) with handle 0xb4000076d55a6960
2023-04-24 11:08:47.860 28184-28184 Choreographer com.rnskiacrash I Skipped 45 frames! The application may be doing too much work on its main thread.
2023-04-24 11:08:47.869 28184-8145 OpenGLRenderer com.rnskiacrash I Davey! duration=784ms; Flags=0, IntendedVsync=26011652765731, Vsync=26011652765731, OldestInputEvent=9223372036854775807, NewestInputEvent=0, HandleInputStart=26011653152334, AnimationStart=26011653153271, PerformTraversalsStart=26012433451630, DrawStart=26012433543818, SyncQueued=26012433688037, SyncStart=26012434428818, IssueDrawCommandsStart=26012434592620, SwapBuffers=26012436153193, FrameCompleted=26012438023922, DequeueBufferDuration=343594, QueueBufferDuration=1144427, GpuCompleted=25959886312958,
2023-04-24 11:08:48.561 28184-28184 Choreographer com.rnskiacrash I Skipped 36 frames! The application may be doing too much work on its main thread.
2023-04-24 11:08:48.611 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_CRITICAL) received by JS VM, running a GC
2023-04-24 11:08:48.770 28184-8193 ReactNativeJNI com.rnskiacrash I Memory warning (pressure level: TRIM_MEMORY_RUNNING_LOW) received by JS VM, ignoring because it's non-severe
2023-04-24 11:08:49.504 1726-2405 InputDispatcher system_server E channel 'b285455 com.rnskiacrash/com.rnskiacrash.MainActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
In this video, you can see the app increasing the memory usage until it crashes. It takes a while so I had to cut it short, but you can still notice how the memory usage grows. https://user-images.githubusercontent.com/39544919/234285127-d3d6657b-ed07-4fef-83d9-3e09b2e57d0c.mp4
public afterMount(): void {
setTimeout(this.toggle, 1000);
}
@bind
public toggle(): void {
this.setState({visible: !Boolean(this.state.visible)}, () => {
setTimeout(this.toggle, 1);
});
}
public render(): any {
if (this.state.visible) {
return (
<Canvas key={'canvas'} style={{ width: '100%', height: 300, display: 'flex'}}>
<Circle cx={128} cy={128} r={128} color="lightblue"/>
</Canvas>
);
}
}
I confirm, this code crashes the application in 30 seconds. Android 9 Version 0.1.193
checked version 0.1.176, the application crashes there too
Update There is no memory leak when using SkiaView
<SkiaView onDraw={this.onDraw}/>
The example was not possible to run out of the box, but I created this example component:
export const Breathe = () => {
const [visible, setVisible] = React.useState(true);
const update = () => {
setTimeout(() => {
setVisible((v) => !v);
update();
}, 1000);
};
useEffect(() => {
update();
}, []);
return visible ? (
<Canvas
key={"canvas"}
style={{ width: "100%", height: 300, display: "flex" }}
>
<Circle cx={128} cy={128} r={128} color="lightblue" />
</Canvas>
) : null;
};
When run in Android it refreshes every second, and the memory stays pretty stable over time (we see a small increase in memory but no crash - how long do you need to run this to see the crash?
With your interval of 1000 milliseconds, I need to wait 1000 times longer, because in my case the interval is 1 millisecond.
1000 * 30 = 30000 seconds 30000 / 60 = 500 minutes 500 / 60 = 8,3 hours
Your example will crash in only 8 hours..
In a real application from production, the frequency of re-creating the canvas reaches ~10+ times per second
Makes sense, I just took the numbers from your example where you used 1000 ms as the timeout :) :)
We would definetly look for another way to solve this than by recreating the Canvas so often. Could you describe your use case a bit more?
<VerticalList>
<HorizontalList>[canvas1][canvas2]</HorizontalList>
<HorizontalList>[canvas1][canvas2]</HorizontalList>
<HorizontalList>[canvas1][canvas2]</HorizontalList>
<HorizontalList>[canvas1][canvas2]</HorizontalList>
...
</VerticalList>
When scrolling HorizontalList to the right, the canvas jumps into view. When changing the screen, all these canvases are deleted, others are created, which leads to the crash of the application within ~ 10 minutes of continuous use.
Update Specifically for me, the task is not relevant.
I rewrote my project to the imperative style and SkiaView, as a result of which the memory leaks disappeared, and the code began to work faster than through Canvas.
@hitman249 Could you please share your leak-free implementation? I'm curious what the imperative style looks like
@cjhines
@hitman249 Could you please share your leak-free implementation? I'm curious what the imperative style looks like
An example is in the documentation Custom Drawing
@hitman249 I would be curious to see the code that runs faster in SkiaView
than Canvas
. I would also be curious to look at the suspected memory leak
In my issue discussing the crash present in the workaround, I observed that even an empty canvas is gradually accumulating memory.
https://github.com/Shopify/react-native-skia/assets/16542421/ddbd4357-a7d4-40d5-8003-aaf59c56f1aa
@cjhines do you have a small reproducible example for this?
@wcandillon Sure! Here's a repo: https://github.com/cjhines/skia-memory-example
By repeatedly opening and closing the second screen with the buttons on the top, you will see memory consumption growing rapidly.
In our real app this is even worse due to SVG variety.
https://github.com/Shopify/react-native-skia/assets/16542421/4b2c986f-7dd9-4e42-8616-b204c1c428d4
Thanks, @cjhines - I'm looking into this - the example is a bit big and we'd like to be able to reduce any number of potential causes for this to fail, would it be possible to reduce it to a single source file without any other dependencies than RN Skia (and Reanimated + navigation (stack/tabs)) if possible? Would help us a lot.
@chrfalch Hugely appreciated! I've pushed a commit to simplify the project to be contained in App.tsx
and removed many unrelated dependencies.
Thanks, appreciate it!