jsc-android-buildscripts: Android memory leak due to JSC

related to: https://github.com/facebook/react-native/issues/23259; cc @akshetpandey

Issue Description

A full GC is performed only when Heap#overCriticalMemoryThreshold returns true or if the default is changed to disable the generational GC. Otherwise only the young generation is collected. Unfortunately, overCriticalMemoryThreshold is only implemented for iOS and always returns false for other platforms. This means that any objects promoted from young to old are not GC’d and the app will eventually crash due to an out of memory error.

Please note that running RN through the Chrome debugger does not use JSC. This will use Chrome’s JS engine and promptly reclaim memory. We confirmed this bug and fix by reducing the maximum memory for JSC, observing the leak, and then healthy behavior when resolved.

Version, config, any additional info

The least invasive change was to force a full collection and we are satisfied with the resulting performance. In our application, we did not experience long pause times that would result in a negative experience. We changed overCriticalMemoryThreshold to return true on non-iOS platforms and believe that is the correct default behavior. A more advanced solution would be to add Android support to calculate if the threshold was crossed.

iff --git a/webkit/Source/JavaScriptCore/heap/Heap.cpp b/webkit/Source/JavaScriptCore/heap/Heap.cpp
index ffeac67d..9dcf4113 100644
--- a/webkit/Source/JavaScriptCore/heap/Heap.cpp
+++ b/webkit/Source/JavaScriptCore/heap/Heap.cpp
@@ -498,7 +498,7 @@ bool Heap::overCriticalMemoryThreshold(MemoryThresholdCallType memoryThresholdCa
     return m_overCriticalMemoryThreshold;
 #else
     UNUSED_PARAM(memoryThresholdCallType);
-    return false;
+    return true;
 #endif
 }

We substitute the android-jsc dependency with our patched version,

// replace the webkit jsc package with react native with our customized only do full 
// garbage collection to avoid the memory leak issue in js heap
configurations.all {
  resolutionStrategy.dependencySubstitution {
    substitute module('org.webkit:android-jsc') with module('com.withvector:android-jsc:r225067')
  }
}

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 6
  • Comments: 39 (10 by maintainers)

Most upvoted comments

Thanks @ben-manes for your report. We discussed this issue internally on contributors discord and with help from some jsc maintainers figured one other approach to fix this. We are hoping to find some time to investigate it and patch soon

I had similar issues after upgrading to 0.59.x (but not sure if the problems existed before upgrade). Anyways RN 0.59.10 did not solve the issue even though JSC is updated in that version.

But then I ran into this: https://www.npmjs.com/package/react-native-v8

A drop-in replacement for JSC using V8. By quick testing it really seems to just work just by installing and adding those few lines to build scripts.

And best of all it solved memory issues!

Blog post about all the different JS engines: https://dev.to/anotherjsguy/react-native-memory-profiling-jsc-vs-v8-vs-hermes-1c76

@Kudo We modified your code to increase the interval from 1ms to 5s and also increased the buffer from 1K to 50MB. So every 5s, allocates 50MB of buffer and after 250MB deallocates everything. We tested this on Android 8 (API 26).

  • The default.jsc contains the logs for default jsc that comes with react native 0.58.3.
  • The custom.full.gc.jsc uses are patched version (above)

android8.api26.log.tar.gz

android8.api26
├── custom.full.gc.jsc
│   ├── logcat.txt
│   └── memory.usage.log.txt
├── default.jsc
│   ├── logcat.txt
│   └── memory.usage.log.txt
└── diff.txt