Garbage Collector hangs app in Android


#1

Hello, I have made a simple example to reproduce an issue with garbage collection.
In the example, accessing to the kotlin library and forcing gc (It happens also without forcing it) makes the app hang and use ~100% of cpu.
I don’t know if it could be a v8 issue. I would like some advice on how to solve it, because this is making my app unresponsive during undetermined time when a garbage collection happens.

Here is a link to the project, It should be visible using and emulator, and clearly a problem in a real device:
https://drive.google.com/open?id=0Bx_IBINpIoEKcmtHbEdiLUdoU2c

GitHub repository: https://github.com/hiperbou/testNSgc

gc


#2

Hey @hiperbou is it possible that you upload the project to a GitHub repository, for easier browsing and navigation? Thanks!

Also please note that we’ve made no efforts to support Kotlin in the NativeScript Android runtime, so unexpected problems may, and will most likely occur.


#3

Here it is, but there is not much code to browse :slight_smile:


#4

Hey, what is the expected behavior of the following code?

(function (_, Kotlin) {
  function tap() {
    console.log("Tapped")
    Kotlin;
    console.log("Calling GC")
    global.gc();
    console.log("GC done")
  }
  _.tap = tap;
  return _;
}(module.exports, require('kotlin')));

I am not sure how the npm kotlin module is intended to be used. But simply requiring it, and doing numerous GCs afterwards did not seem to impact my hello-world app’s performance.
This is what my onTap handler looks like:

    public onTap() {
        this._counter--;
        this.updateMessage();
        var kotlin = require("kotlin");

        debugger;

        (<any>global).gc();
    }

Do you want to write Kotlin instead of JavaScript? You may consider toying with the k otlin-compiler package in that case. https://www.npmjs.com/package/kotlin-compiler


#5

Thanks for your answer.
Your example is not doing the same. Please, test my code and you can see the delay between log calls.
My code is just wrapped in a commonjs format, because I’m already using Kotlin instead of Javascript, and this js was generated by the Kotlin compiler, later I stripped down manually to the minimal example that shows the GC freeze.
Here is explained the behaviour of my code:
-When touching the button, a “Tapped” log is written to the console
-The next line is an access to the object Kotlin, passed by parameter, with the value of require(‘kotlin’)
-Next, a call to the gc is forced
-We have to wait here… probably you can see some logcat messages about high cpu usage…
-Then “GC done” is written to the console.

If you comment in my code the line “Kotlin” (line 4) you can guess a GC watching the ActivityIndicator freeze for some miliseconds.
On the other hand, in your code, adding an access to “kotlin” works Ok.

So my guess is that there is some problem passing the value returned from the require call as a parameter.


#6

I’ve removed more code from the example, so now it looks like this:

kotlin = require("kotlin");
exports.tap =   function tap() {
    console.log("Calling GC")
    global.gc();
    console.log("GC done")
}

#7

Ok, now I’ve searched for the most depended library wich turns to be lodash
https://www.npmjs.com/browse/depended

And the exact test is showing the freeze. For greater happiness dare to repeat pressing the button in an android device.
Loading more or bigger libraries will increase the freeze time. :frowning:

lodash = require("lodash");
exports.tap =   function tap() {
    console.log("Calling GC")
    global.gc();
    console.log("GC done")
}

Similar results can be achieved by requiring “nativescript-angular/nativescript.module”


#8

Forcing the garbage collector will indeed cause a freeze, but certainly not as huge as shown in the screenshot you initially posted. What point are you trying to make?

Longer version:
Manually invoking gc() often will cause a slow-down, or even freeze your application. Calls to gc, I have observed, to incur anywhere from 50-150ms main-thread blocking for js/ts apps, to 200-1000ms blocking for unoptimized angular applications.

That is due to the fact that the garbage collection routine iterates through all object references in order to look for JavaScript proxies to Java objects which may be marked for collection in Java world, but there are valid references to them in JavaScript world, and thus need to be preserved.

Having said that, it is recommended that JavaScript gc() be run when the application has the time for it (for example if going idle for a moment), as it can take a considerable amount of time to complete.


#9

Well I can see this is the source of my headache :slight_smile: I can guess that in the initial case, the kotlin library is big enought to make this process take for extremly long. So I can’t do a gc, while the user is interacting, so at the moment I will take a manual approach to gc
This approach of looking for proxies through all the references seems an overkill. Can this be tuned? It whould be nice to be able to skip references of pure js libraries that should not contain Java proxies.


#10

I looked through Kotlin, and it isn’t big enough to cause a freeze that exaggerated though.

Given how dynamic JavaScript is, you cannot distinguish at runtime whether a script contains just pure JavaScript, or whether it could contain proxy objects. Not without executing it first, or all of its methods for that matter, since JS can be executed lazily. You can’t just skip a purely JavaScript object/function scope, because you don’t know whether it doesn’t hold a reference to a Java proxy indirectly through another JavaScript reference. It’s a complex matter, and one that we’ve given much thought in the past, and tinker constantly to improve or replace. However in order to allow you to work with native APIs directly through JavaScript, some compromises need to be made, and we need to make sure that we don’t release Java/JavaScript references by mistake.

Here’s a blog post by one of my colleagues who worked on the current GC routine implementation

https://iobservable.net/category/gc/


#11

Well… Just if anyone having this problem, It turns out that there is a configuration to disable this behaviour as explained here:

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

#12

PLEASE NOTE

The configuration is experimental. Until the configuration becomes a default, or is announced officially as being stable, and documentation has been provided on how to migrate the code, you use it at your own discretion.