56

I'm curious if there is any good way to unload a module after using it. I have some cases where I need to use modules that bring in a lot of code, but they are used rarely (say as an admin tool), but I hesitate to use them because afterwards they'll presumably just waste memory that could be better used elsewhere. Is there any way to unload them, either explicitly or by allowing the system to do so when they haven't been used for a while?

rob
  • 9,933
  • 7
  • 42
  • 73

2 Answers2

79

Yes, it is possible to access the module cache directly:

var name = require.resolve('moduleName');
delete require.cache[name];

Note that if your code carries a reference to whatever was exposed by these modules you want to get rid of, it won't be cleaned up.

(As an aside: Underneath the surface, require.resolve and require.cache are just proxies to Module._resolveFilename and Module._cache respectively, with Module being the core module loader, i.e. require('module').)

chjj
  • 14,322
  • 3
  • 32
  • 24
  • 12
    This isn't a complete answer. Some requires load an index.js file, which then load the real meat of the library. As an example, try require('async') this loads "...async/lib/async.js", but also loads "..async/index.js", which you can see in the require.cache. index.js is the parent of async.js. So it's probably possible to unregister, but may require a recursive function to delete multiple modules. – Aneil Mallavarapu Mar 13 '12 at 03:48
  • 4
    Just do `for (var key in Object.keys(require.cache)) {delete require.cache[key];}`. – Tower Jul 13 '12 at 21:05
  • 5
    @Tower, That would unload all the modules. It might be better to try to check `module.parent` - or something - and only unload the modules you want to unload. For example, `util` and `fs` probably should not be unloaded. – 700 Software Dec 17 '12 at 15:41
  • Why should util and fs not be unloaded specifically? – B T Jun 29 '13 at 22:49
  • Just a speculation though, I think most modules are stateless, usually if it requires any state the requiring module will have to invoke some constructor function like e.g. `bunyan.createLogger` to make a stateful instance. So I think unloading everything should generally not cause any problem for the majority of cases. (as simply requiring something should not by itself cause unrelated global state changes anyway) – chakrit Aug 19 '13 at 09:34
  • 1
    This works particularly well if you're loading a JSON model/template, which you may need to use multiple times (as opposed to using a fs.readFile and subsequent JSON.parse) – HiDefLoLife Feb 10 '17 at 01:42
7

You can do something like this to "unload" a node module:

/**
 * Deletes a node module and all associated children
 * from node require cache
 * @param {string} moduleName The name of the module or 
 *                            absolute/relative path to it
 */
function deleteModule(moduleName) {
  var solvedName = require.resolve(moduleName),
    nodeModule = require.cache[solvedName];
  if (nodeModule) {
    for (var i = 0; i < nodeModule.children.length; i++) {
      var child = nodeModule.children[i];
      deleteModule(child.filename);
    }
    delete require.cache[solvedName];
  }
}

"Unloading" a node module means that you're "deleting" its entry from node require cache. The snippet above transitively deletes one node module from the cache and all the associated modules (children modules).

In my case I was requiring "index.js" that, at the same time, was requiring "/lib/.js". I wanted to test initialization of "index.js" (and therefore the only other module being required in there which was /lib/.js). Deleting "index.js" from the cache was not enough because the cache maintained a separate reference to "/lib/.js" and that caused the module "/lib/.js" to not be loaded again from its file.

The code above might certainly delete from the cache other modules this module depends on (this is actually what I wanted BUT you might not want to do this for other modules like "fs", "path" or even "underscore").

You can always extend this method to accept a list of modules you don't want to delete or a filter you could apply to the "path" of the resolved module. You can say: don't delete core node modules (a list of core node modules names is available as a npm package: https://www.npmjs.com/package/node-core-module-names) or don't delete any of the packages under node_modules, etc etc.. This could be a filter inside the function but it's the same logic.

cSn
  • 2,796
  • 3
  • 23
  • 29