Skip to content

Conversation

PanayotCankov
Copy link
Contributor

Allows the MarkReachableObjects to be suppressed.
Setting:

{
  "android": {
    "markingMode": "none"
  }
}

Will completely disable the JavaScript heap traversal in MarkReachableObjects. This saves about 500ms. block on the main thread (we've seen it go up to 1350ms during navigation)

The memory management model changes drastically:

  • When a Java instance marshalled to a JavaScript type, the Java and JavaScript instances are bound,

    • Add finaliser to the JavaScript instance
    • Add a strong reference to the Java instance (this will not let it be garbage collected)
  • When the JavaScript instance's finaliser is called (the JavaScript instance is not reachable)

    • Make the reference to the Java instance weak (this will let it be garbage collected by Java GC)
    • Check the weak reference to the Java instance, if the Java instance is still alive, don't let the JavaScript instance be collected

This approach can lead to instances being prematurely collected, but doesn't involve long blocks on the main thread.

We want to run excessive testing on our apps so we can get a better understanding of the implications following from suppression MarkReachableObjects.

@NativeScript NativeScript deleted a comment from ns-bot Aug 21, 2017
@NativeScript NativeScript deleted a comment from dtopuzov Aug 21, 2017
@NativeScript NativeScript deleted a comment from ns-bot Aug 21, 2017
@NativeScript NativeScript deleted a comment from ns-bot Aug 21, 2017

public int getMarkingMode() {
String mode = staticConfiguration == null || staticConfiguration.appConfig == null ? "full" : staticConfiguration.appConfig.getMarkingMode();
if ("none".equals(mode)) {
Copy link
Contributor

@Plamen5kov Plamen5kov Aug 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make enumeration with values of marking mode

m_numberOfGC(0),
m_currentObjectId(0),
m_cache(NewWeakGlobalRefCallback, DeleteWeakGlobalRefCallback, 1000, this),
m_markingMode(JavaScriptMarkingMode::Full) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get marking mode from java static configuration, don't set it as default here, because it's easier to change defaults only in one place

return dynamicConfig;
}

public int getMarkingMode() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add @RuntimeCallable on method

jint markingMode = m_env.CallIntMethod(m_javaRuntimeObject, getMarkingModeMethodID);
switch(markingMode) {
case 1:
m_markingMode = JavaScriptMarkingMode::None;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: this means every new thread will have this value reinitialized, so potentially values may differ between threads.

jsInfo = prototypeObject->GetInternalField(jsInfoIdx);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extract repeating code:

        const int jsInfoIdx = static_cast<int>(MetadataNodeKeys::JsInfo);
        auto jsInfo = object->GetInternalField(jsInfoIdx);
        if (jsInfo->IsUndefined()) {
            //Typescript object layout has an object instance as child of the actual registered instance. checking for that
            auto prototypeObject = object->GetPrototype().As<Object>();
            if (!prototypeObject.IsEmpty() && prototypeObject->IsObject()) {
                DEBUG_WRITE("GetJSInstanceInfo: need to check prototype :%d", prototypeObject->GetIdentityHash());
                if (IsJsRuntimeObject(prototypeObject)) {
                    jsInfo = prototypeObject->GetInternalField(jsInfoIdx);
                }
            }
        }

used in ObjectManager::GetJSInstanceInfo

}
}

if (jsInfo.IsEmpty() || !jsInfo->IsExternal()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!jsInfo->IsExternal() check is redundant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be "undefined".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or at least it used to be before we augmented the previous PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok in that case leave IsEmpty and IsUndefined, but in both cases they implicitly imply !IsExternal

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDK, the whole thing got reorganised when I extracted a "GetJSInstanceInfoFromRuntimeObject" from the "GetJSInstanceInfo" and this one. Take a look now.

}

if (jsInfo.IsEmpty() || !jsInfo->IsExternal()) {
// The JavaScript instance has been forcefully disconnected from the Java instance.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please provide a better comment as this one implies we are setting jsInfo to undefined or empty

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may add explicit "release()" method in future (although highly unlikely). But it also does some sanity check like for the preconditions that we have to check and you mention two lines below.

return;
}

auto external = jsInfo.As<External>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check !jsInfo.IsEmpty() || jsInfo->IsExternal()) before cast

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually the method 2 lines above does that check.

Copy link
Contributor

@Plamen5kov Plamen5kov Aug 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

before the cast is made there needs to be explicit check for IsExternal

thisPtr->JSObjectFinalizer(isolate, callbackState);
}

void ObjectManager::JSObjectFinalizer(Isolate* isolate, ObjectWeakCallbackState* callbackState) {
Copy link
Contributor

@Plamen5kov Plamen5kov Aug 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add Handle scope in the beginning of the method to take care of the local handles:

auto isolate = m_isolate;
HandleScope handleScope(isolate);

jboolean isJavaInstanceAlive = m_env.CallBooleanMethod(m_javaRuntimeObject, MAKE_INSTANCE_WEAK_AND_CHECK_IF_ALIVE_METHOD_ID, javaObjectID);
if (isJavaInstanceAlive) {
// If the Java instance is alive, keep the JavaScript instance alive.
// TODO: Check should we really register the finalizer again?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this TODO: is obsolete, remove it, if it's not let's check it out before it gets merged into master.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was added in the previous implementation in JSObjectWeakCallback so I guess it should be there.

@NativeScript NativeScript deleted a comment from ns-bot Aug 24, 2017
@NativeScript NativeScript deleted a comment from dtopuzov Aug 24, 2017
@NativeScript NativeScript deleted a comment from ns-bot Aug 24, 2017
@NativeScript NativeScript deleted a comment from dtopuzov Aug 24, 2017
@NativeScript NativeScript deleted a comment from ns-bot Aug 24, 2017
@NativeScript NativeScript deleted a comment from dtopuzov Aug 24, 2017
@NativeScript NativeScript deleted a comment from ns-bot Aug 24, 2017
@NativeScript NativeScript deleted a comment from dtopuzov Aug 24, 2017
@NativeScript NativeScript deleted a comment from ns-bot Aug 24, 2017
…to switch the memory management to one, that will not traverse the JS heap graph
Copy link
Contributor

@Plamen5kov Plamen5kov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check out the comments

@PanayotCankov PanayotCankov force-pushed the marking-mode-none branch 2 times, most recently from 49d0556 to 0e6e7e3 Compare August 24, 2017 12:10
@NativeScript NativeScript deleted a comment from ns-bot Aug 24, 2017
@NativeScript NativeScript deleted a comment from ns-bot Aug 24, 2017
@PanayotCankov
Copy link
Contributor Author

run ci

@NativeScript NativeScript deleted a comment from ns-bot Aug 24, 2017
@ns-bot
Copy link

ns-bot commented Aug 24, 2017

💚


if (jsInfo.IsEmpty() || !jsInfo->IsExternal()) {
// The JavaScript instance has been forcefully disconnected from the Java instance.
if (!jsInstanceInfo) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in case jsInstanceInfo is nullptr this check wont pass
please change check to: jsInstanceInfo != nullptr

Profiling,
MarkingMode
};
MarkingMode("markingMode", com.tns.MarkingMode.values()[0]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

com.tns.MarkingMode.full as default value instead of com.tns.MarkingMode.values()[0]

return (String)values[KnownKeys.Profiling.ordinal()];
}
public String getMarkingMode() { return (String)values[KnownKeys.MarkingMode.getIndex()]; }
public MarkingMode getMarkingMode() { return (MarkingMode)values[KnownKeys.MarkingMode.ordinal()]; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for cast (MarkingMode)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The values is of type Object[], indexing it returns Object.

if (staticConfiguration != null && staticConfiguration.appConfig != null) {
return staticConfiguration.appConfig.getMarkingMode().ordinal();
} else {
return MarkingMode.values()[0].ordinal();
Copy link
Contributor

@Plamen5kov Plamen5kov Aug 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use KnownKeys.MarkingMode.getDefaultValue() here, or if you don't want to expose KnownKeys just expose API from app config that gets the default and current if any.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I exposed KnownKeys, I think it is fine.

try {
String value = androidObject.getString(KnownKeys.MarkingMode.getName());
values[KnownKeys.MarkingMode.ordinal()] = MarkingMode.valueOf(value);
} catch(Throwable e) {
Copy link
Contributor

@Plamen5kov Plamen5kov Aug 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • catch Exception instead of throwable here if you don't want to have a specific warning for each exception, otherwise catch the specific exceptions thrown my methods.
  • don't leave catch block empty, please leave a warning that the default value will be used because we couldn't get the value for some reason

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Printed the stack trace and then a message with Log.v("JS", ... that the default will be used.

Copy link
Contributor

@Plamen5kov Plamen5kov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check out comments.
ordinal() is a great addition.

rootObject = FileSystem.readJSONFile(packageInfo);
if (rootObject != null) {
if (rootObject.has(KnownKeys.Profiling.getName())) {
if (rootObject.has(KnownKeys.Profiling.name())) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be getName()

Copy link
Contributor

@Plamen5kov Plamen5kov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After green build

@NativeScript NativeScript deleted a comment from ns-bot Aug 25, 2017
@NativeScript NativeScript deleted a comment from ns-bot Aug 25, 2017
@NativeScript NativeScript deleted a comment from ns-bot Aug 25, 2017
@NativeScript NativeScript deleted a comment from ns-bot Aug 25, 2017
@NativeScript NativeScript deleted a comment from ns-bot Aug 25, 2017
@PanayotCankov
Copy link
Contributor Author

run ci

@NativeScript NativeScript deleted a comment from ns-bot Aug 25, 2017
@NativeScript NativeScript deleted a comment from ns-bot Aug 25, 2017
@ns-bot
Copy link

ns-bot commented Aug 25, 2017

💚

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants