mobx: Large observable arrays do not play well with React Native Android
I’m not sure if this is Android bug or MobX, but here it is:
Generally, we’ve noticed that our RN app has problems handling large arrays - but only on Android devices, and only when not debugging remotely. That points it pretty much to Android JS VM, but, it might be MobX problem as well.
After tons of debugging, I’ve been able to distill minimal example showing the problem.
react-native create mobxtest
cd mobxtest
yarn add mobx
Now, in index.android.js in mobxtest class add this:
@observable foo = [];
constructor( props, context ){
super( props, context );
let f = ()=>{
let ff = [ { a: 10 }, { b: 20 }, { c: 30 } ];
ff = ff.concat( ff );
ff = ff.concat( ff );
ff = ff.concat( ff );
ff = ff.concat( ff );
ff = ff.concat( ff );
ff = ff.concat( ff );
ff = ff.concat( ff );
ff = ff.concat( ff );
ff = ff.concat( ff );
ff = ff.concat( ff ); // remove this
this.foo = ff;
console.log('APEEK1', this.foo[10], ff[ 10 ], this.foo.peek()[10]);
console.log('APEEK2', this.foo[1000], ff[ 1000 ], this.foo.peek()[1000]);
};
setInterval( f, 2000 );
}
import { observable } from 'mobx';
Now, react-native run-android and with adb logcat *:S ReactNativeJS see the output. Here it goes:
01-05 19:51:49.746 12358 13327 I ReactNativeJS: Running application "mobxtest" with appParams: {"initialProps":{},"rootTag":1}. __DEV__ === true, development-level warning are ON, performance optimizations are OFF
01-05 19:51:51.826 12358 13327 I ReactNativeJS: 'APEEK1', { b: [Getter/Setter] }, { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:51:51.826 12358 13327 I ReactNativeJS: 'APEEK2', { b: [Getter/Setter] }, { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:51:53.806 12358 13327 I ReactNativeJS: 'APEEK1', { b: [Getter/Setter] }, { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:51:53.806 12358 13327 I ReactNativeJS: 'APEEK2', { b: [Getter/Setter] }, { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:51:55.836 12358 13327 I ReactNativeJS: 'APEEK1', { b: [Getter/Setter] }, { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:51:55.836 12358 13327 I ReactNativeJS: 'APEEK2', { b: [Getter/Setter] }, { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:51:57.836 12358 13327 I ReactNativeJS: 'APEEK1', { b: [Getter/Setter] }, { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:51:57.846 12358 13327 I ReactNativeJS: 'APEEK2', { b: [Getter/Setter] }, { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:51:59.846 12358 13327 I ReactNativeJS: 'APEEK1', { b: [Getter/Setter] }, { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:51:59.846 12358 13327 I ReactNativeJS: 'APEEK2', { b: [Getter/Setter] }, { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:52:01.866 12358 13327 I ReactNativeJS: 'APEEK1', { b: [Getter/Setter] }, { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:52:01.866 12358 13327 I ReactNativeJS: 'APEEK2', undefined, { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:52:03.856 12358 13327 I ReactNativeJS: 'APEEK1', { b: [Getter/Setter] }, { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:52:03.856 12358 13327 I ReactNativeJS: 'APEEK2', [], { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:52:05.866 12358 13327 I ReactNativeJS: 'APEEK1', { b: [Getter/Setter] }, { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:52:05.866 12358 13327 I ReactNativeJS: 'APEEK2', [], { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:52:07.876 12358 13327 I ReactNativeJS: 'APEEK1', { b: [Getter/Setter] }, { b: [Getter/Setter] }, { b: [Getter/Setter] }
01-05 19:52:07.876 12358 13327 I ReactNativeJS: 'APEEK2', [], { b: [Getter/Setter] }, { b: [Getter/Setter] }
and it goes on for a bit, then app usually crashes.
I’m using Samsung Galaxy S7, Android 6.0.1, RN 0.40 & RN 0.39, MobX 2.7.0 and 2.6.5 (our app is RN 0.39 + MobX 2.6.5, reproduced on RN 0.40 + MobX 2.7.0).
If the array is shorter (like the commented line is removed), it usually works well.
We do see the bad behaviour in much, much complex case (loading >1000 contacts from device’s address book, then displaying them in ListView). Lots of lodash, etc. But I’ve been able to untangle it down to this simple equivalent. In our app, we’ve also seen pretty strange crashes with MobX warnings about attempting to read element like 2081 out of 1531 possible, etc. That was basically on _.filter( foo, (c)=> ... ) whereas foo was observable array. The source seem to be the same, MobX started to severely misbehave.
I think there is something wrong with the JS JIT, but that is only a remote shot.
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 3
- Comments: 33 (15 by maintainers)
I’ve got my own custom code that was using
Object.definePropertyon an array. Unlike mobx I don’t preset the array with a certain length but increase the length as needed. I was getting random errors in various points of my app on android exactly as you were/are @Nopik. It would work sometimes with debug JS turned off but it would still crash quite often and on an array with 2 or 3 items in it. If I define all the keys from the start or redefine them when increasing the length of the array it would work.It seems this is an issue with the JIT compiler on android that react-native uses as it’s rather old (2014). So I ended up following the instructions here for the SoftwareMansion/jsc-android-buildscripts library that allows for using a more modern JavaScript engine instead of the one included with react-native and voila, my problems have gone away. I’m guessing it should work for you as well. Hopefully others that run into this issue will also see this post as I spent quite some time first to find the issue in the first place since it doesn’t crash immediately at the spot where the array is changed. Anyway, thanks for your efforts here because it pointed me in the right direction.
@ragamufin I encountered the same issue and your solution works great, thanks
Just thought I’d mention the following here in case it helps anyone, as this issue seems to be one of the only places on the internet that combines MobX with
0xbbadbeeffaults, and I did already use a custom JSC build.I don’t have large observable arrays, but I do have a few nested ones. I noticed that changing observables frequently by tapping around the screen like a mad man would crash my app within 10-30 seconds, with (usually, not always) a
0xbbadbeeffault address. This is in a Android 6.0 emulator, and to be completely honest, I have not tried to reproduce this in later versions.Relevant environment details:
Solution: I’ve since upgraded
jsc-androidto225067.0.0, and I have yet to see it crash in my emulator. I also no longer get JVM garbage collection logs in adb nearly as much anymore.Is there any reproducible setup? Also curious:
@observable foo = asFlat([])does that make a difference? But given the irregularity of the output, (sometimes printingundefinedor[]), I suspect this is a VM bug indeed… 😢