re-natal
re-natal copied to clipboard
Out Of Memory Errors on Android
Hey there! Thanks so much for such a cool project :) I've been looking to try out ClojureScript and Reagent/Re-frame on a fun project, and this seemed like a great way to try.
However, I noticed when the app runs in Dev mode (haven't tried production), it takes up a great deal of memory and after a few reloads causes the app in my Android emulator to crash. (Note: I tried both the default Android Emulator and Genymotion).
Not sure if anyone has ideas on the source of this problem or how easy it would be to fix up, but this looked like a bit of a show-stopper for a first time user. Thanks again for all the time and effort on this project!
Steps:
- Follow the instructions to get the app running on Android
- Reload the android app several times (opening the menu, "Reload JS")
- App will crash (adb log shows an Out Of Memory Trend)
Looking at the monitor, there's a ton of memory usage from the sample app. Here's a comparison of the default re-natal memory usage vs react-native memory usage:
Stock React Native Project: Memory Usage
The blips in the graph come from reloading. Notice it always stays around 3mb of memory usage.

The increases come after every time I reload the project. Notice the memory continues to grow and grow:

Eventually resulting in this Out Of Memory Crash:
--------- beginning of crash
01-10 13:50:00.885 19749-21983/com.gol E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
Process: com.gol, PID: 19749
java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack trace available
01-10 13:50:00.892 19749-19757/com.gol E/System: java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack trace available
[ 01-10 13:50:00.895 1362: 1835 D/ ]
HostConnection::get() New Host Connection established 0xa07b7340, tid 1835
01-10 13:50:00.911 19749-19749/com.gol I/art: Clamp target GC heap from 67MB to 64MB
That's a bummer! Thanks for reporting this. I have tried reloading it severat times, and yes, after 5-7 reloads it crashes. Also on real Android device. On iOS memory also grows, but app does not crash, probably it is allowed to allocate so much memory.
Looks like prod-build is not affected. I also have tested with older version of re-natal hoping to find that it is caused by some of my recent changes in figwheel-bridge.js but same problem was there. Suspect that it might be caused by not using React Native's require function for loading the js code.
Could it be related to the way how fighweel updates are handled?
I don't know re-natal code very well but I suspect that you call something like AppRegistry.registerComponent
or registerRunnable
every time when there is a new change coming from the figwheel. And it may happened that previous React applications are still alive after that
It should be easy to test - just comment those line and try couple of times to reload the app. I don't think that there is any leaks in figwheel-bridge (at least that big)
@artemyarulin, thanks, I will check app initialization. But, actually, the problem is not the reload by figwheel (on recompile, well there might be a problem too, I need to check it), but when you reload the whole app manuall via Reload JS dev menu many times. During the initial load app loads a lot of js files asyncronously and evaluates them using eval. I guess the same approach for initial js code loading is used in https://github.com/artemyarulin/ktoa project. Have you tried to reload Android app several times by using Reload JS menu?
Hm, well I've reloaded android app like 10 times, but everything was fine (not sure maybe it wasn't enough, I have 16GB RAM. I'm using Genymotion Nexus 5 emulator with 2GB base memory. Too lazy to install Android Studio to run a profiler :))
But this is weird - I guess reload app should re-create JS context and free all the allocated memory. It makes sense at least. BTW - do you modify native react native app?
That is great news, thanks! I also would extect that from Reload JS, its funny. React native app stays untouched by re-natal setup scripts, and I would like to keep that this way. But, maybe its a logo image somehow leaks through the reload... need to check it :)
Doh! :) Looks like without image android monitor tool shows a lot less used memory. And reloading the app does not result in vast memory increase. @brianegan could you please confirm that memory is ok when image is removed? You have to remove following lines:
[image {:source logo-img
:style {:width 80 :height 80 :margin-bottom 30}}]
Hm, it still could be that the whole app leaks and image is just a biggest part of it. I guess you are using standard js/require
for the image and in this method call there is nothing that can go wrong I assume. I should check it for ktoa as well
Yes, you might be right, I had no time to dig deeper, but I will.
Not exactly, locally stored images are currently referenced by cutom macro in re-natal which expands to normal js/require
in prod-build and to {:url http://localhost:8081/path/to/image.jpg} (just like any external image) during dev. This was done targeting scenario when you just drop images to a folder and just use them in your code without any restart. I planned to decomission the macro and use same approach as for external components for images (which is how it should be done) if dev experience is not harmed too much.
Had some time to experiment, and currently it looks like in development mode with each Reload JS some objects are really leaking. I have dumped heap several times after some reloading and checked with analyser the leak suspect report. Leaking objects are at least these:
- com.facebook.react.uimanager.UIManagerModule
- com.facebook.react.bridge.CatalystInstanceImpl
Number of UIManagerModule instances is same as CatalystInstanceImpl instances and constantly growing. So far I have no idea why this happens.



Hey all, sorry to drop off a for a days there -- busy times :) I've tested the app by removing the image, and indeed, the memory footprint is stable at around 16mb from a clean boot until after 5 or 6 live reloads. Therefore, to confirm the theory, the leak appears to originate from usage of the image component.
Screenshot of memory footprint after removing the image:

@drapanjanas: Have you played with AppRegistry.registerComponent/registerRunnable? According to AppRegistry docs there is a method called unmountApplicationComponentAtRootTag
maybe it would help
Well I only did one quick test, I've found that in dev mode AppRegistry.registerComponent is called two times. And registerRunnable is not called at all in re-natal app. First time registerComponent is called from index.android.js (from fihwgeel-bridge.js module) and loads the cljs app ansynchronously in componentDidMount lifecycle function. Second time AppRegistry.registerComponent is called in core/init function:
(defn init []
(dispatch-sync [:initialize-db])
(.registerComponent app-registry "ReagentApp" #(r/reactify-component app-root)))
I have tried to comment out that call and did a quick test with external image https://raw.githubusercontent.com/cljsinfo/logo.cljs/master/cljs.png. App crashed after several reloads.
I did not know about unmountApplicationComponentAtRootTag
thanks for this info
Thanks so much for taking a look at this, all!
It looks like leak is only happens when you do "Reload JS" when figwheel REPL connection is alive. I have tried stopping figwheel REPL, then memory is freed after each reload. So it might be some bug related to websockets in RN, but I have no proof of that yet. Also good news is that changes to images which are done in code and hot loaded by figwheel, like removing image, adding it again, changing url of image, are not causing this leak. So if you reloading your app often for whatever reason, and experience app crashes, try also stopping REPL session from time to time before you do "Reload JS".