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

Potential memory leak with arrays in shared values #2824

Closed
1 of 3 tasks
vabruzzese opened this issue Jan 11, 2022 · 18 comments
Closed
1 of 3 tasks

Potential memory leak with arrays in shared values #2824

vabruzzese opened this issue Jan 11, 2022 · 18 comments
Labels
bug-bash-jan22 Issues visited during Bug Bash Jan 2022 Needs review Issue is ready to be reviewed by a maintainer Platform: iOS This issue is specific to iOS Repro provided A reproduction with a snippet of code, snack or repo is provided

Comments

@vabruzzese
Copy link

Description

I'm noticing a spike in memory usage over time when updating a shared value in react-native-reanimated-2. I suspect that it may be an issue in C++ when deleting array pointer references.

Expected behavior

Memory usage should stay the same over time. I left this code running for 10 minutes and checked the memory usage through xcode and it was much higher.

Screen Shot 2022-01-11 at 4 14 12 PM

Shareable value pointers should be cleared up when re-assigning the value but instead we are seeing 1131065 shared value pointer references:

Screen Shot 2022-01-11 at 4 20 10 PM

Actual behavior & steps to reproduce

Create a react-native-reanimated-2 playground that reassigns a shared value to an object with an array and set an interval to continuously update it

Snack or minimal code example

import {useSharedValue} from 'react-native-reanimated';
import {View, Text} from 'react-native';
import React, {useEffect, useState} from 'react';

export default function AnimatedStyleUpdateExample(props) {
  const [obj, setObj] = useState();

  const randomObject = () => {
    const arr = [];
    for (let i = 0; i < 10000; i++) {
      arr.push({
        key: i,
        value: Math.random(),
      });
    }

    return {
      id: Math.random(),
      arr,
    };
  };

  const test = useSharedValue();

  useEffect(() => {
    const interval = setInterval(() => {
      setObj(randomObject());
    }, 500);

    return () => clearInterval(interval);
  }, []);

  useEffect(() => {
    test.value = obj;
  }, [obj, test]);

  return (
    <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
      <Text>Hello World</Text>
      <Text>{test.value ? test.value.id : ''}</Text>
    </View>
  );
}

Package versions

name version
react-native 0.65.1
react-native-reanimated 2.3.1
NodeJS 17.2.0
Xcode 13.2.1
Java
Gradle
expo

Affected platforms

  • Android
  • iOS
  • Web
@vabruzzese vabruzzese added the Needs review Issue is ready to be reviewed by a maintainer label Jan 11, 2022
@github-actions github-actions bot added Platform: iOS This issue is specific to iOS Repro provided A reproduction with a snippet of code, snack or repo is provided labels Jan 11, 2022
@tapz
Copy link

tapz commented Jan 12, 2022

I'm also seeing this in iOS from several users:

The OS most likely terminated your app because it overused RAM.

@tapz
Copy link

tapz commented Jan 18, 2022

Seeing this quite frequently from multiple users:

Screenshot 2022-01-18 at 20 05 16

Screenshot 2022-01-18 at 20 05 20

@j-piasecki j-piasecki added the bug-bash-jan22 Issues visited during Bug Bash Jan 2022 label Jan 28, 2022
@amsimon
Copy link

amsimon commented May 12, 2022

Seeing this issue on iOS. Using Reanimated with arrays is a pretty standard use case and makes it unusable for me.

What is the status on this?

@eric-edouard
Copy link

Can confirm we're seeing important memory leaks on arrays in shared values as well

@vabruzzese
Copy link
Author

Any updates here? The library is pretty unusable in this state.

@marksyzm
Copy link

marksyzm commented Sep 3, 2022

I've concluded it's happening with all versions. I'm having to resort to state which is just about okay, but really needs looking into.

@marksyzm
Copy link

marksyzm commented Sep 3, 2022

Temporary fix: #3304 (comment)

@kmagiera
Copy link
Member

We started investigating this issue and after some testing it seems like there is something wacky happening on the JS VM garbage collector side. Manually triggering garbage collector on both UI and RN JS VMs results in all the objects being cleared up. We'd normally expect the GC to kick in at some point and dealloc all those hanging objects, however it seems like that's not the case and in our testing we cal also see that even after significant time we still don't get these objects cleaned up property. We will continue this investigation and share more updates as we discover more details about this issue.

@marksyzm
Copy link

marksyzm commented Sep 16, 2022

@kmagiera Do you have any extra info for how you're triggering the garbage collecting so we can work with that in the meantime?

@kmagiera
Copy link
Member

I'm not sure if there exists a way with JSC to trigger garbage collector programmatically. We use a connected debugger that has a button for starting a GC. If you use hermes you can do hermes.gc() but this function is not available on the UI JS runtime and adding it requires a tiny patch to reanimated that we applied locally for now.

@kmagiera
Copy link
Member

Here is the patch that makes it possible to call hermes' GC from the UI thread:

diff --git a/Common/cpp/Tools/RuntimeDecorator.cpp b/Common/cpp/Tools/RuntimeDecorator.cpp
index fbc35f754..9fd7c88d4 100644
--- a/Common/cpp/Tools/RuntimeDecorator.cpp
+++ b/Common/cpp/Tools/RuntimeDecorator.cpp
@@ -31,6 +31,7 @@ void RuntimeDecorator::decorateRuntime(
       rt, "_LABEL", jsi::String::createFromAscii(rt, label));
 
   jsi::Object dummyGlobal(rt);
+  dummyGlobal.setProperty(rt, "gc", rt.global().getProperty(rt, "gc"));
   rt.global().setProperty(rt, "global", dummyGlobal);
 
   rt.global().setProperty(rt, "jsThis", jsi::Value::undefined());

@kmagiera
Copy link
Member

Then you need to trigger gc at some moment both on the main JS and UI threads, for example with this method:

import { runOnUI } from 'react-native-reanimated';

function triggerGC() {
  global.gc();
  runOnUI(() => {
    'worklet';
    global.gc();
  })();
}

@vabruzzese
Copy link
Author

@kmagiera glad to hear that some progress is being made. From my end, I tried to grab the nightly release and trigger this garbage collection function but unfortunately, I'm not seeing any memory get cleared up after doing so:

Simulator.Screen.Recording.-.iPhone.13.-.2022-09-20.at.11.20.30.mp4

I'm guessing that there may still be some pointer references at the time of triggering this function that is preventing the old values from being cleared up.

@kmagiera
Copy link
Member

hey @VinceAbruzzese – the patch I posted hasn't been merged to the library so pulling latest version won't make the gc function available on the UI thread.

@kmagiera
Copy link
Member

also it seem that tapping GC button more than once (specifically three times seem to give the best outcome) gives the best results.

@marksyzm
Copy link

marksyzm commented Oct 2, 2022

@kmagiera Is there a branch I can follow, please?
Oh and is this the Hades gc? I just spotted they use that now:
https://hermesengine.dev/docs/hades

@kmagiera kmagiera mentioned this issue Oct 26, 2022
6 tasks
piaskowyk pushed a commit that referenced this issue Nov 29, 2022
## Description

This PR contains a new implementation of the "shareable value" concept – one of the lowest level key concepts behind shared values/worklets etc.

There were many reasons for attempting this rewrite, but the main objective was to address memory-related issues rooted in that core part and difficult to fix due to an inherent complexity of that bit of the codebase. Below, I list a couple of reasons providing some more details description:

1. The aforementioned memory issues can be best noted on #2824 – the root cause of that and similar issue lies in the way we reference objects between the two runtimes. Due to the fact we use JSI's host objects and direct references it turns out that a lot of objects can outlast garbage collection because, for example they are referenced on the secondary runtime where garbage collection didn't run yet. Moreover, JSI objects have their own, simplified version of GC that has to run as well. As a result, for some objects to be cleaned up properly, we needed to run GC on the main JS runtime, then on the UI runtime and once again on the main JS runtime.
2. Secondary objective for the rewrite was to address the maintainability of the codebase part being rewritten. After investigating the issue mentioned above we concluded that it is too difficult and risky to try to fix it given the architectural choices we made in the past.
3. As a side-effect of the things being rewritten we expect the library to perform better. When tracking performance issues in the past, we detected that a lot of issues are due to an excessive communication over JSI. JSI is a lot quicker compared to calling things over "the bridge", however it still incurs some performance penalty that is significant enough on lower level devices even at a level of couple hundred calls. This issue has became apparent to us at some point, but the existing architecture wouldn't allow us to address it in a easy way. As an example we can bring up the issue with worklet functions that, before this rewrite, were represented as JSI's HostFunctions. As a result, calling worklet from on the UI thread would require a JSI roundtrip, and that has became problematic as we expanded the codebase and had one more logic extracted out into smaller worklets. This rewrite has been architected to optimize the number of cross-JSI calls.
4. The new core makes it possible to implement some new and often requested functionalities. Specifically, the ability to make complex shareable objects, that is, to be able to append to a shared array or add/remove entries from a shared map as opposed to always having to copy the whole new object with the modifications applied. Adding this also helps to improve some bits internally, specifically the case of shared styles, where we have a single animated style object assigned to multiple views. Before, we'd use an immutable array that'd represent the list of so-called view descriptors. With this rewrite we can add and remove entries without a need to copy the whole descriptors array as we mount new views.

The description of this PR is also a work in progress and will update sections of this description as the work progresses.

## Changes

This PR changes a lot of things under the hood. The main change lies in a way we create and reference shareable values. As part of this change the whole "notion" of shareable values is removed from the core and implemented on top of a different abstraction. We now allow for "shareable" data to provide an initializer function which is called on the UI runtime in order to instantiate data that then will be accessed by worklets. In addition, we avoid keeping JSI object cache on c++ side, this prevents issues with them being referenced for longer than necessary and hence was a root cause of memory related issues (leaks -- but these weren't actual leaks as triggering GC several times would result in a complete cleanup).
@alexstanbury
Copy link

Then you need to trigger gc at some moment both on the main JS and UI threads, for example with this method:

import { runOnUI } from 'react-native-reanimated';

function triggerGC() {
  global.gc();
  runOnUI(() => {
    'worklet';
    global.gc();
  })();
}

Trying this on iOS and it seems to help but doesn't clean up fully. But on Android I get instant crash:

Type	Time	PID	Tag	Message
	12:43:20.210	17232	libc	Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 17232 (myappname.debug), pid 17232 (myappname.debug)
	12:43:20.255	18664	crash_dump64	obtaining output fd from tombstoned, type: kDebuggerdTombstoneProto
	12:43:20.259	272	tombstoned	received crash request for pid 17232
	12:43:20.260	18664	crash_dump64	performing dump of process 17232 (target tid = 17232)
	12:43:20.766	225	logd	logdr: UID=10158 GID=10158 PID=18664 n tail=0 logMask=8 pid=17232 start=0ns deadline=0ns
	12:43:20.767	225	logd	logdr: UID=10158 GID=10158 PID=18664 n tail=0 logMask=1 pid=17232 start=0ns deadline=0ns
	12:43:20.772	18664	DEBUG	*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

	12:43:20.772	18664	DEBUG	Build fingerprint: 'google/sdk_gphone64_arm64/emu64a:13/TPB4.220624.004/8808248:userdebug/dev-keys'

	12:43:20.772	18664	DEBUG	Revision: '0'

	12:43:20.772	18664	DEBUG	ABI: 'arm64'

	12:43:20.772	18664	DEBUG	Timestamp: 2022-12-01 12:43:20.262887302+0000

	12:43:20.772	18664	DEBUG	Process uptime: 2962s

	12:43:20.772	18664	DEBUG	Cmdline: co.uk.myappname.debug

	12:43:20.772	18664	DEBUG	pid: 17232, tid: 17232, name: myappname.debug  >>> co.uk.myappname.debug <<<

	12:43:20.772	18664	DEBUG	uid: 10158

	12:43:20.772	18664	DEBUG	tagged_addr_ctrl: 0000000000000001 (PR_TAGGED_ADDR_ENABLE)

	12:43:20.772	18664	DEBUG	pac_enabled_keys: 000000000000000f (PR_PAC_APIAKEY, PR_PAC_APIBKEY, PR_PAC_APDAKEY, PR_PAC_APDBKEY)

	12:43:20.772	18664	DEBUG	signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------

	12:43:20.772	18664	DEBUG	    x0  0000000000000000  x1  0000000000004350  x2  0000000000000006  x3  0000007fddb3da00

	12:43:20.772	18664	DEBUG	    x4  0000000040033763  x5  0000000040033763  x6  0000000040033763  x7  00000000bfb9915e

	12:43:20.772	18664	DEBUG	    x8  00000000000000f0  x9  00000077bed9aa00  x10 0000000000000001  x11 00000077bedd8ce4

	12:43:20.772	18664	DEBUG	    x12 0000007fddb3c508  x13 0000000000000000  x14 0000000000000004  x15 0000000000000004

	12:43:20.772	18664	DEBUG	    x16 00000077bee3dd60  x17 00000077bee1ab70  x18 00000077c5ef0000  x19 0000000000004350

	12:43:20.772	18664	DEBUG	    x20 0000000000004350  x21 00000000ffffffff  x22 ffffff80224c183f  x23 0000007fddb3dc60

	12:43:20.772	18664	DEBUG	    x24 0000007fddb3e7c0  x25 0000000000000000  x26 0000000000000001  x27 0000007fddb3e7c0

	12:43:20.772	18664	DEBUG	    x28 0000007fddb3e6c0  x29 0000007fddb3da80

	12:43:20.772	18664	DEBUG	    lr  00000077bedca868  sp  0000007fddb3d9e0  pc  00000077bedca894  pst 0000000000001000

	12:43:20.772	18664	DEBUG	backtrace:

	12:43:20.772	18664	DEBUG	      #00 pc 0000000000051894  /apex/com.android.runtime/lib64/bionic/libc.so (abort+164) (BuildId: 058e3ec96fa600fb840a6a6956c6b64e)

	12:43:20.772	18664	DEBUG	      #01 pc 00000000000af7e4  /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/lib/arm64/libc++_shared.so (BuildId: ece72a2ebc3774a1be9fd21271258acd3bcdfaa7)

	12:43:20.772	18664	DEBUG	      #02 pc 00000000000aedc8  /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/lib/arm64/libc++_shared.so (BuildId: ece72a2ebc3774a1be9fd21271258acd3bcdfaa7)

	12:43:20.772	18664	DEBUG	      #03 pc 00000000000aebf8  /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/lib/arm64/libc++_shared.so (__gxx_personality_v0+200) (BuildId: ece72a2ebc3774a1be9fd21271258acd3bcdfaa7)

	12:43:20.772	18664	DEBUG	      #04 pc 000000000022bd4c  /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/lib/arm64/libreanimated.so (BuildId: 82c0e59bc1713ddfef44d9aed5062b13b6812f7d)

	12:43:20.772	18664	DEBUG	      #05 pc 000000000022be08  /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/lib/arm64/libreanimated.so (BuildId: 82c0e59bc1713ddfef44d9aed5062b13b6812f7d)

	12:43:20.772	18664	DEBUG	      #06 pc 0000000000181c04  /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/lib/arm64/libreanimated.so (facebook::jsi::Function::call(facebook::jsi::Runtime&, facebook::jsi::Value const*, unsigned long) const+144) (BuildId: 82c0e59bc1713ddfef44d9aed5062b13b6812f7d)

	12:43:20.772	18664	DEBUG	      #07 pc 00000000001b73c4  /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/lib/arm64/libreanimated.so (BuildId: 82c0e59bc1713ddfef44d9aed5062b13b6812f7d)

	12:43:20.772	18664	DEBUG	      #08 pc 00000000001b6fe8  /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/lib/arm64/libreanimated.so (BuildId: 82c0e59bc1713ddfef44d9aed5062b13b6812f7d)

	12:43:20.772	18664	DEBUG	      #09 pc 00000000001b6f9c  /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/lib/arm64/libreanimated.so (BuildId: 82c0e59bc1713ddfef44d9aed5062b13b6812f7d)

	12:43:20.772	18664	DEBUG	      #10 pc 00000000001b6f74  /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/lib/arm64/libreanimated.so (BuildId: 82c0e59bc1713ddfef44d9aed5062b13b6812f7d)

	12:43:20.772	18664	DEBUG	      #11 pc 00000000001b5fdc  /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/lib/arm64/libreanimated.so (BuildId: 82c0e59bc1713ddfef44d9aed5062b13b6812f7d)

	12:43:20.772	18664	DEBUG	      #12 pc 00000000001a5a64  /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/lib/arm64/libreanimated.so (BuildId: 82c0e59bc1713ddfef44d9aed5062b13b6812f7d)

	12:43:20.772	18664	DEBUG	      #13 pc 00000000001a5690  /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/lib/arm64/libreanimated.so (std::__ndk1::function<void ()>::operator()() const+20) (BuildId: 82c0e59bc1713ddfef44d9aed5062b13b6812f7d)

	12:43:20.772	18664	DEBUG	      #14 pc 00000000001e4ad8  /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/lib/arm64/libreanimated.so (reanimated::Scheduler::triggerUI()+80) (BuildId: 82c0e59bc1713ddfef44d9aed5062b13b6812f7d)

	12:43:20.772	18664	DEBUG	      #15 pc 00000000001e9f74  /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/lib/arm64/libreanimated.so (reanimated::AndroidScheduler::triggerUI()+36) (BuildId: 82c0e59bc1713ddfef44d9aed5062b13b6812f7d)

	12:43:20.772	18664	DEBUG	
	12:43:20.772	18664	DEBUG	
	12:43:20.772	18664	DEBUG	
	12:43:20.772	18664	DEBUG	      #19 pc 00000000001ea700  /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/lib/arm64/libreanimated.so (facebook::jni::detail::MethodWrapper<void (reanimated::AndroidScheduler::*)(), &(reanimated::AndroidScheduler::triggerUI()), reanimated::AndroidScheduler, void>::call(_JNIEnv*, _jobject*)+36) (BuildId: 82c0e59bc1713ddfef44d9aed5062b13b6812f7d)

	12:43:20.772	18664	DEBUG	      #20 pc 0000000000440354  /apex/com.android.art/lib64/libart.so (art_quick_generic_jni_trampoline+148) (BuildId: 97fdb979efb7d2b596fa4fceabaad95b)

	12:43:20.772	18664	DEBUG	      #21 pc 000000000020a2b0  /apex/com.android.art/lib64/libart.so (nterp_helper+4016) (BuildId: 97fdb979efb7d2b596fa4fceabaad95b)

	12:43:20.772	18664	DEBUG	      #22 pc 00000000000bddb8  [anon:dalvik-classes2.dex extracted in memory from /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/base.apk!classes2.dex] (com.swmansion.reanimated.Scheduler$1.run+28)

	12:43:20.772	18664	DEBUG	      #23 pc 000000000020b074  /apex/com.android.art/lib64/libart.so (nterp_helper+7540) (BuildId: 97fdb979efb7d2b596fa4fceabaad95b)

	12:43:20.772	18664	DEBUG	      #24 pc 00000000000bddf8  [anon:dalvik-classes2.dex extracted in memory from /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/base.apk!classes2.dex] (com.swmansion.reanimated.Scheduler$2.runGuarded+12)

	12:43:20.772	18664	DEBUG	      #25 pc 000000000020a254  /apex/com.android.art/lib64/libart.so (nterp_helper+3924) (BuildId: 97fdb979efb7d2b596fa4fceabaad95b)

	12:43:20.772	18664	DEBUG	      #26 pc 000000000043011c  [anon:dalvik-classes.dex extracted in memory from /data/app/~~32x9fdvFxAgP7YeDzxj6vg==/co.uk.myappname.debug--RB3w5v9mY9O29U9wwaeqQ==/base.apk] (com.facebook.react.bridge.GuardedRunnable.run+0)

	12:43:20.772	18664	DEBUG	      #27 pc 00000000020c6374  /memfd:jit-cache (deleted) (android.os.Handler.handleCallback+100)

	12:43:20.772	18664	DEBUG	      #28 pc 00000000020c41c0  /memfd:jit-cache (deleted) (android.os.Handler.dispatchMessage+96)

	12:43:20.772	18664	DEBUG	      #29 pc 00000000020c4904  /memfd:jit-cache (deleted) (android.os.Looper.loopOnce+1076)

	12:43:20.772	18664	DEBUG	      #30 pc 0000000000209a9c  /apex/com.android.art/lib64/libart.so (nterp_helper+1948) (BuildId: 97fdb979efb7d2b596fa4fceabaad95b)

	12:43:20.772	18664	DEBUG	      #31 pc 0000000000497886  /system/framework/framework.jar (android.os.Looper.loop+162)

	12:43:20.772	18664	DEBUG	      #32 pc 0000000000209334  /apex/com.android.art/lib64/libart.so (nterp_helper+52) (BuildId: 97fdb979efb7d2b596fa4fceabaad95b)

	12:43:20.772	18664	DEBUG	      #33 pc 00000000001bb65e  /system/framework/framework.jar (android.app.ActivityThread.main+202)

	12:43:20.772	18664	DEBUG	      #34 pc 0000000000436c00  /apex/com.android.art/lib64/libart.so (art_quick_invoke_static_stub+576) (BuildId: 97fdb979efb7d2b596fa4fceabaad95b)

	12:43:20.772	18664	DEBUG	      #35 pc 0000000000469334  /apex/com.android.art/lib64/libart.so (_jobject* art::InvokeMethod<(art::PointerSize)8>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long)+1960) (BuildId: 97fdb979efb7d2b596fa4fceabaad95b)

	12:43:20.772	18664	DEBUG	      #36 pc 0000000000468b64  /apex/com.android.art/lib64/libart.so (art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*) (.__uniq.165753521025965369065708152063621506277)+48) (BuildId: 97fdb979efb7d2b596fa4fceabaad95b)

	12:43:20.772	18664	DEBUG	      #37 pc 0000000000099148  /system/framework/arm64/boot.oat (art_jni_trampoline+120) (BuildId: ea692a544a012e5ae0589def08d76ae73b248ab5)

	12:43:20.772	18664	DEBUG	      #38 pc 000000000020a2b0  /apex/com.android.art/lib64/libart.so (nterp_helper+4016) (BuildId: 97fdb979efb7d2b596fa4fceabaad95b)

	12:43:20.772	18664	DEBUG	      #39 pc 00000000003f71de  /system/framework/framework.jar (com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run+22)

	12:43:20.772	18664	DEBUG	      #40 pc 00000000007bc52c  /system/framework/arm64/boot-framework.oat (com.android.internal.os.ZygoteInit.main+3228) (BuildId: 1549e9510d42e3841c5e4c3800af4daa83cc39bb)

	12:43:20.772	18664	DEBUG	      #41 pc 0000000000436c00  /apex/com.android.art/lib64/libart.so (art_quick_invoke_static_stub+576) (BuildId: 97fdb979efb7d2b596fa4fceabaad95b)

	12:43:20.772	18664	DEBUG	      #42 pc 0000000000582140  /apex/com.android.art/lib64/libart.so (art::JValue art::InvokeWithVarArgs<_jmethodID*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)+900) (BuildId: 97fdb979efb7d2b596fa4fceabaad95b)

	12:43:20.772	18664	DEBUG	      #43 pc 00000000005f5e14  /apex/com.android.art/lib64/libart.so (art::JNI<true>::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+160) (BuildId: 97fdb979efb7d2b596fa4fceabaad95b)

	12:43:20.772	18664	DEBUG	      #44 pc 00000000000bcad0  /system/lib64/libandroid_runtime.so (_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+120) (BuildId: 652257cd0faef901accde2659193d1c3)

	12:43:20.772	18664	DEBUG	      #45 pc 00000000000c85c4  /system/lib64/libandroid_runtime.so (android::AndroidRuntime::start(char const*, android::Vector<android::String8> const&, bool)+840) (BuildId: 652257cd0faef901accde2659193d1c3)

	12:43:20.772	18664	DEBUG	      #46 pc 0000000000002554  /system/bin/app_process64 (main+1280) (BuildId: a1ab812b262121cb66f7cbe228dc9674)

	12:43:20.772	18664	DEBUG	      #47 pc 000000000004a0f4  /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+96) (BuildId: 058e3ec96fa600fb840a6a6956c6b64e)

fluiddot pushed a commit to wordpress-mobile/react-native-reanimated that referenced this issue Jun 5, 2023
## Description

This PR contains a new implementation of the "shareable value" concept – one of the lowest level key concepts behind shared values/worklets etc.

There were many reasons for attempting this rewrite, but the main objective was to address memory-related issues rooted in that core part and difficult to fix due to an inherent complexity of that bit of the codebase. Below, I list a couple of reasons providing some more details description:

1. The aforementioned memory issues can be best noted on software-mansion#2824 – the root cause of that and similar issue lies in the way we reference objects between the two runtimes. Due to the fact we use JSI's host objects and direct references it turns out that a lot of objects can outlast garbage collection because, for example they are referenced on the secondary runtime where garbage collection didn't run yet. Moreover, JSI objects have their own, simplified version of GC that has to run as well. As a result, for some objects to be cleaned up properly, we needed to run GC on the main JS runtime, then on the UI runtime and once again on the main JS runtime.
2. Secondary objective for the rewrite was to address the maintainability of the codebase part being rewritten. After investigating the issue mentioned above we concluded that it is too difficult and risky to try to fix it given the architectural choices we made in the past.
3. As a side-effect of the things being rewritten we expect the library to perform better. When tracking performance issues in the past, we detected that a lot of issues are due to an excessive communication over JSI. JSI is a lot quicker compared to calling things over "the bridge", however it still incurs some performance penalty that is significant enough on lower level devices even at a level of couple hundred calls. This issue has became apparent to us at some point, but the existing architecture wouldn't allow us to address it in a easy way. As an example we can bring up the issue with worklet functions that, before this rewrite, were represented as JSI's HostFunctions. As a result, calling worklet from on the UI thread would require a JSI roundtrip, and that has became problematic as we expanded the codebase and had one more logic extracted out into smaller worklets. This rewrite has been architected to optimize the number of cross-JSI calls.
4. The new core makes it possible to implement some new and often requested functionalities. Specifically, the ability to make complex shareable objects, that is, to be able to append to a shared array or add/remove entries from a shared map as opposed to always having to copy the whole new object with the modifications applied. Adding this also helps to improve some bits internally, specifically the case of shared styles, where we have a single animated style object assigned to multiple views. Before, we'd use an immutable array that'd represent the list of so-called view descriptors. With this rewrite we can add and remove entries without a need to copy the whole descriptors array as we mount new views.

The description of this PR is also a work in progress and will update sections of this description as the work progresses.

## Changes

This PR changes a lot of things under the hood. The main change lies in a way we create and reference shareable values. As part of this change the whole "notion" of shareable values is removed from the core and implemented on top of a different abstraction. We now allow for "shareable" data to provide an initializer function which is called on the UI runtime in order to instantiate data that then will be accessed by worklets. In addition, we avoid keeping JSI object cache on c++ side, this prevents issues with them being referenced for longer than necessary and hence was a root cause of memory related issues (leaks -- but these weren't actual leaks as triggering GC several times would result in a complete cleanup).
@piaskowyk
Copy link
Member

Fixed with #3722 - The fix available in Reanimated 3 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug-bash-jan22 Issues visited during Bug Bash Jan 2022 Needs review Issue is ready to be reviewed by a maintainer Platform: iOS This issue is specific to iOS Repro provided A reproduction with a snippet of code, snack or repo is provided
Projects
None yet
Development

No branches or pull requests

9 participants