Sage Gerard
Sage Gerard

Reputation:

How does garbage collection address async actions in mobx-utils?

I want a clear method for creating stores that I can refresh when I know new service data is available (like lazyObservable from mobx-utils), but easily attach more computed values and action functions.

Starting from create-react-app, use this index.js.

import React from 'react';
import ReactDOM from 'react-dom';
import {observable} from 'mobx';
import {observer} from 'mobx-react';
import {asyncAction} from 'mobx-utils';

const flipACoin = () => Math.random() > 0.5;

function determineGodliness() {
  const screwYourRAM = new Array(1000000).fill(Math.random());

  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([flipACoin(), flipACoin()]);
    }, 1500);
  });
}

function godStore() {
  const store = observable({
    alpha: false,
    omega: false,
    waiting: false,
    refresh: asyncAction(function *() {
      this.waiting = true;
      [this.alpha, this.omega] = yield determineGodliness();
      this.waiting = false;
    }),
    get isGod() {
      return this.alpha && this.omega;
    }
  });

  store.refresh();

  return store;
}

window.store = godStore();

const App = observer(({store}) => <p>{
  (store.waiting)
    ? 'The suspense is killing me!'
    : (store.isGod)
      ? 'I am the Alpha and the Omega'
      : 'I just work here.'
}</p>);

ReactDOM.render(<App store={window.store} />, document.getElementById('root'));

Task manager shows memory usage increase every time you run window.store.refresh() in the console. Interestingly enough, using setInterval(window.store.refresh, 3000) actually causes oscillation in memory usage instead of a linear climb. Between those two conflicting cases I am confused as to how the garbage collector views this setup.

What can I do to be absolutely sure that screwYourRAM will eventually get garbage collected? All I care about keeping is what's returned from the generator, not what was allocated in the interim.

Upvotes: 0

Views: 929

Answers (1)

mweststrate
mweststrate

Reputation: 4978

Memory leaks are hard to determine by looking just at the mem graphs. Afaik V8 will not always release all memory, unless there is a real shortage (GC-ing is an optimization after all, too aggressively collecting will save memory, but burn too many CPU cycles. You can't assume afaik that an idle VM will automatically release all memory). So I usually test it with a node script that keeps running and repeating a process for a long time, and with limited amount of memory to see if something actually leaks.

In your example, as far as I know / can tell, there is no reason why there would be memory leaked. At some point the Promise is gc-ed, making it possible to gc the closures that go with it. The chrome console will keep a ref to your promise through $_ so that might explain the memory increase as well)

To properly see if there are memory leaks, use the profilers in chrome / firefox, and make sure you force a GC (chrome devtools has a button for that, FF probably as well)

Upvotes: 1

Related Questions