Cleanup JavaScript Core in iOS after use

I can create a new JSContext and use this to perform JavaScript operations. But if I want to shut down the JS Core after I have finished with it (without killing the app this is running in) I can find no means of doing that.

Is there a way to get the iOS system to release all memory/resources associated with the JSCore after I have finished with it?

The Apple docs at https://developer.apple.com/documentation/javascriptcore/jscontext don't provide any guidance for how to remove the JS Core after use. Maybe I am missing something.

I did also look at the underlying JSVirtualMemory instance, but again there is no obvious way to shut this down and release all memory.

If I simply dereference the JSContext instance, I would have hoped that this might trigger cleanup but it does not.

If I simply dereference the JSContext instance

Likewise.

How are you testing this?

I created a small test project with the following code:

class MyViewController: UIViewController {

    var context: JSContext? = nil
    
    func start() -> JSContext? {
        guard let context = JSContext() else { return nil }
        let result = context.evaluateScript("1 + 1")
        print(result)
        return context
    }
    
    func stop(context: JSContext) {
        // do nothing
    }

    func startStopAction() {
        if let context = self.context {
            self.context = nil
            self.stop(context: context)
        } else {
            self.context = self.start()
        }
    }
    
    …
}

and wired startStopAction() up to a button. I then did this:

  1. I launched the app.

  2. I stopped it using the Debug Memory Graph button.

  3. I search the memory graph for instances of JSContext and JSVirtualMachine. There were none, which is what I’d expect because I haven’t yet started up JavaScriptCore.

  4. I continued execution and clicked my button. This prints:

    Optional(2)
    

    showing that my context is alive.

  5. I repeated step 3. This time I saw one instance of each class.

  6. I continued execution and clicked my button again, releasing my JSContext reference.

  7. I repeated step 3. I’m now back to no instances of these classes.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

hi - thanks for the very fast response :-)

My set up pushes a large JS file into the core, does some work, and then recycles (by just nullifying the JSContext object, as you do). If I look at memory consumption, it goes up when I insert the javascript into the core and execute JavaScript functions (in my case it jumps by about 100MB as I have a lot of JavaScript code ...) On dereferencing the JSContext and looping round, no discernible drop in memory happens. If I then loop back I end up increasing memory by approx 100MB each time ... so the memory consumption grows a lot :-)

I'll see if I can create a more simple setup and share it. My current app is not really something that is easy to work with :-)

chris

If I then loop back I end up increasing memory by approx 100MB each time

The Leaks instrument has a snapshot feature that’s great for investigating issues like this. The basic idea is:

  1. Run your app.

  2. Run the test.

  3. Snapshot.

  4. Run the test again.

  5. Snapshot.

  6. Repeat steps 4 and 5 a few more times.

You can then compare allocations between the snapshots, to see what objects are still around and why.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi - this was super helpful. Sorry I took so long to get back on this but your suggestions help me identify the retain cycle that prevented the JS Core from being cleaned up properly. I had a thread I was running (handling timers initiated in the JS layer) but when the thread was shut down I did not do it cleanly (I just blindly called Thread.exit()) but I needed to shut the thread properly and let it deallocate any retained references. Once I did that i suddenly saw the retain cycles disappear.

Cleanup JavaScript Core in iOS after use
 
 
Q