Skip to content

Commit

Permalink
Trigger GC and drop compiled code on low memory
Browse files Browse the repository at this point in the history
Reviewed By: astreet

Differential Revision: D2658693

fb-gh-sync-id: 8cba49b67ac45a2dbf8b4c9c404d6fb9c97693f6
  • Loading branch information
lexs authored and facebook-github-bot-5 committed Dec 7, 2015
1 parent 611e061 commit 510d50f
Show file tree
Hide file tree
Showing 12 changed files with 182 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright 2004-present Facebook. All Rights Reserved.

package com.facebook.react;

import javax.annotation.Nullable;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Build;

import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.MemoryPressure;
import com.facebook.react.bridge.ReactContext;

import static android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
import static android.content.ComponentCallbacks2.TRIM_MEMORY_MODERATE;
import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;

/**
* Translates and routes memory pressure events to the current catalyst instance.
*/
public class MemoryPressureRouter {
// Trigger this by sending an intent to your activity with adb shell:
// am start -a "com.facebook.catalyst.ACTION_TRIM_MEMORY" --activity-single-top -n <activity>
private static final String ACTION_TRIM_MEMORY ="com.facebook.catalyst.ACTION_TRIM_MEMORY";

private @Nullable CatalystInstance mCatalystInstance;
private final ComponentCallbacks2 mCallbacks = new ComponentCallbacks2() {
@Override
public void onTrimMemory(int level) {
trimMemory(level);
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
}

@Override
public void onLowMemory() {
}
};

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public static boolean handleDebugIntent(Activity activity, String action) {
switch (action) {
case ACTION_TRIM_MEMORY:
simulateTrimMemory(activity, TRIM_MEMORY_MODERATE);
break;
default:
return false;
}

return true;
}

MemoryPressureRouter(Context context) {
context.getApplicationContext().registerComponentCallbacks(mCallbacks);
}

public void onNewReactContextCreated(ReactContext reactContext) {
mCatalystInstance = reactContext.getCatalystInstance();
}

public void onReactInstanceDestroyed() {
mCatalystInstance = null;
}

public void destroy(Context context) {
context.getApplicationContext().unregisterComponentCallbacks(mCallbacks);
}

private void trimMemory(int level) {
if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
dispatchMemoryPressure(MemoryPressure.CRITICAL);
} else if (level >= TRIM_MEMORY_BACKGROUND || level == TRIM_MEMORY_RUNNING_CRITICAL) {
dispatchMemoryPressure(MemoryPressure.MODERATE);
}
}

private void dispatchMemoryPressure(MemoryPressure level) {
if (mCatalystInstance != null) {
mCatalystInstance.handleMemoryPressure(level);
}
}

private static void simulateTrimMemory(Activity activity, int level) {
activity.getApplication().onTrimMemory(level);
activity.onTrimMemory(level);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
new ConcurrentLinkedQueue<>();
private volatile boolean mHasStartedCreatingInitialContext = false;
private final UIImplementationProvider mUIImplementationProvider;
private final MemoryPressureRouter mMemoryPressureRouter;

private final ReactInstanceDevCommandsHandler mDevInterface =
new ReactInstanceDevCommandsHandler() {
Expand Down Expand Up @@ -215,6 +216,7 @@ protected void onPostExecute(ReactApplicationContext reactContext) {
mBridgeIdleDebugListener = bridgeIdleDebugListener;
mLifecycleState = initialLifecycleState;
mUIImplementationProvider = uiImplementationProvider;
mMemoryPressureRouter = new MemoryPressureRouter(applicationContext);
}

@Override
Expand Down Expand Up @@ -400,6 +402,7 @@ public void onResume(Activity activity, DefaultHardwareBackBtnHandler defaultBac
public void onDestroy() {
UiThreadUtil.assertOnUiThread();

mMemoryPressureRouter.destroy(mApplicationContext);
if (mUseDeveloperSupport) {
mDevSupportManager.setDevSupportEnabled(false);
}
Expand Down Expand Up @@ -539,6 +542,7 @@ private void setupReactContext(ReactApplicationContext reactContext) {

catalystInstance.initialize();
mDevSupportManager.onNewReactContextCreated(reactContext);
mMemoryPressureRouter.onNewReactContextCreated(reactContext);
moveReactContextToCurrentLifecycleState(reactContext);

for (ReactRootView rootView : mAttachedRootViews) {
Expand Down Expand Up @@ -591,6 +595,7 @@ private void tearDownReactContext(ReactContext reactContext) {
}
reactContext.onDestroy();
mDevSupportManager.onReactInstanceDestroyed(reactContext);
mMemoryPressureRouter.onReactInstanceDestroyed();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@

package com.facebook.react.bridge;

import javax.annotation.Nullable;

import java.util.Collection;

import com.facebook.react.bridge.queue.CatalystQueueConfiguration;
Expand Down Expand Up @@ -49,6 +47,8 @@ public interface CatalystInstance {
<T extends NativeModule> T getNativeModule(Class<T> nativeModuleInterface);
Collection<NativeModule> getNativeModules();

void handleMemoryPressure(MemoryPressure level);

/**
* Adds a idle listener for this Catalyst instance. The listener will receive notifications
* whenever the bridge transitions from idle to busy and vice-versa, where the busy state is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,11 @@ public Collection<NativeModule> getNativeModules() {
return mJavaRegistry.getAllModules();
}

@Override
public void handleMemoryPressure(MemoryPressure level) {
Assertions.assertNotNull(mBridge).handleMemoryPressure(level);
}

/**
* Adds a idle listener for this Catalyst instance. The listener will receive notifications
* whenever the bridge transitions from idle to busy and vice-versa, where the busy state is
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright 2004-present Facebook. All Rights Reserved.

package com.facebook.react.bridge;

public enum MemoryPressure {
MODERATE,
CRITICAL
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,19 @@ public void dispose() {
super.dispose();
}

public void handleMemoryPressure(MemoryPressure level) {
switch (level) {
case MODERATE:
handleMemoryPressureModerate();
break;
case CRITICAL:
handleMemoryPressureCritical();
break;
default:
throw new IllegalArgumentException("Unknown level: " + level);
}
}

private native void initialize(
JavaScriptExecutor jsExecutor,
ReactCallback callback,
Expand All @@ -72,4 +85,6 @@ private native void initialize(
public native boolean supportsProfiling();
public native void startProfiler(String title);
public native void stopProfiler(String title, String filename);
private native void handleMemoryPressureModerate();
private native void handleMemoryPressureCritical();
}
16 changes: 16 additions & 0 deletions ReactAndroid/src/main/jni/react/Bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ class JSThreadState {
m_jsExecutor->stopProfiler(title, filename);
}

void handleMemoryPressureModerate() {
m_jsExecutor->handleMemoryPressureModerate();
}

void handleMemoryPressureCritical() {
m_jsExecutor->handleMemoryPressureCritical();
}

private:
std::unique_ptr<JSExecutor> m_jsExecutor;
Bridge::Callback m_callback;
Expand Down Expand Up @@ -109,4 +117,12 @@ void Bridge::stopProfiler(const std::string& title, const std::string& filename)
m_threadState->stopProfiler(title, filename);
}

void Bridge::handleMemoryPressureModerate() {
m_threadState->handleMemoryPressureModerate();
}

void Bridge::handleMemoryPressureCritical() {
m_threadState->handleMemoryPressureCritical();
}

} }
2 changes: 2 additions & 0 deletions ReactAndroid/src/main/jni/react/Bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class Bridge : public Countable {
bool supportsProfiling();
void startProfiler(const std::string& title);
void stopProfiler(const std::string& title, const std::string& filename);
void handleMemoryPressureModerate();
void handleMemoryPressureCritical();
private:
Callback m_callback;
std::unique_ptr<JSThreadState> m_threadState;
Expand Down
4 changes: 4 additions & 0 deletions ReactAndroid/src/main/jni/react/Executor.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class JSExecutor {
};
virtual void startProfiler(const std::string &titleString) {};
virtual void stopProfiler(const std::string &titleString, const std::string &filename) {};
virtual void handleMemoryPressureModerate() {};
virtual void handleMemoryPressureCritical() {
handleMemoryPressureModerate();
};
virtual ~JSExecutor() {};
};

Expand Down
16 changes: 16 additions & 0 deletions ReactAndroid/src/main/jni/react/JSCExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
#include <JavaScriptCore/API/JSProfilerPrivate.h>
#endif

#ifdef WITH_JSC_MEMORY_PRESSURE
#include <jsc_memory.h>
#endif

#ifdef WITH_FBSYSTRACE
#include <fbsystrace.h>
using fbsystrace::FbSystraceSection;
Expand Down Expand Up @@ -182,6 +186,18 @@ void JSCExecutor::stopProfiler(const std::string &titleString, const std::string
#endif
}

void JSCExecutor::handleMemoryPressureModerate() {
#ifdef WITH_JSC_MEMORY_PRESSURE
JSHandleMemoryPressure(this, m_context, JSMemoryPressure::MODERATE);
#endif
}

void JSCExecutor::handleMemoryPressureCritical() {
#ifdef WITH_JSC_MEMORY_PRESSURE
JSHandleMemoryPressure(this, m_context, JSMemoryPressure::CRITICAL);
#endif
}

void JSCExecutor::flushQueueImmediate(std::string queueJSON) {
m_flushImmediateCallback(queueJSON);
}
Expand Down
4 changes: 3 additions & 1 deletion ReactAndroid/src/main/jni/react/JSCExecutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ class JSCExecutor : public JSExecutor {
virtual bool supportsProfiling() override;
virtual void startProfiler(const std::string &titleString) override;
virtual void stopProfiler(const std::string &titleString, const std::string &filename) override;

virtual void handleMemoryPressureModerate() override;
virtual void handleMemoryPressureCritical() override;

void flushQueueImmediate(std::string queueJSON);
void installNativeHook(const char *name, JSObjectCallAsFunctionCallback callback);

Expand Down
13 changes: 13 additions & 0 deletions ReactAndroid/src/main/jni/react/jni/OnLoad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,16 @@ static void stopProfiler(JNIEnv* env, jobject obj, jstring title, jstring filena
bridge->stopProfiler(fromJString(env, title), fromJString(env, filename));
}

static void handleMemoryPressureModerate(JNIEnv* env, jobject obj) {
auto bridge = extractRefPtr<Bridge>(env, obj);
bridge->handleMemoryPressureModerate();
}

static void handleMemoryPressureCritical(JNIEnv* env, jobject obj) {
auto bridge = extractRefPtr<Bridge>(env, obj);
bridge->handleMemoryPressureCritical();
}

} // namespace bridge

namespace executors {
Expand Down Expand Up @@ -840,6 +850,9 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
makeNativeMethod("supportsProfiling", bridge::supportsProfiling),
makeNativeMethod("startProfiler", bridge::startProfiler),
makeNativeMethod("stopProfiler", bridge::stopProfiler),
makeNativeMethod("handleMemoryPressureModerate", bridge::handleMemoryPressureModerate),
makeNativeMethod("handleMemoryPressureCritical", bridge::handleMemoryPressureCritical),

});

jclass nativeRunnableClass = env->FindClass("com/facebook/react/bridge/queue/NativeRunnable");
Expand Down

0 comments on commit 510d50f

Please sign in to comment.