Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8223426

Regular expressions in scripts are causing memory leaks

    Details

      Description

      A DESCRIPTION OF THE PROBLEM :
      The generated classes for scripts containing JS regular expressions hold static references to NativeRegExp instances, which each holds a strong reference on the Global instance that was active when the script was first evaluated. As a consequence, the Global used for the first evaluation of the script is kept in memory as long as the NashornScriptEngine or the CompiledScript is not garbage collected.

      It appears that the private field globalObject in NativeRegExp is used to store the last match in the Global (via globalObject.setLastRegExpResult) in order to support some non-standard properties of the RegExp object like RegExp.lastMatch and RegExp.$1-$9. Instead of passing Global.instance() to the NativeRegExp constructor, leading to the memory leak, shouldn't Global.instance() be evaluated on demand when a match is about to be stored in the current Global?

      The attached source code demonstrates how an arbitrary object put into a Bindings is never garbage collected even though we do not hold references to the Bindings or the object itself. If we do not execute compiledScript.eval(bindings) with the regular expression script, everything works fine.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      * Create an engine.
      * Compile a script with a regular expression in it.
      * Create a Bindings and run the compiled script from the previous step on it.
      * Put one or more objects into the Bindings.
      * Release all strong references to the Bindings and the objects you have put into it.
      * Trigger GC.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Removing all references to the Bindings and the included objects is sufficient make the Global garbage collectable.
      ACTUAL -
      The Global used for the first script execution is kept in memory and not GCed.

      ---------- BEGIN SOURCE ----------
      import java.lang.ref.WeakReference;

      import javax.script.Bindings;
      import javax.script.CompiledScript;

      import jdk.nashorn.api.scripting.NashornScriptEngine;
      import jdk.nashorn.api.scripting.NashornScriptEngineFactory;

      public class RegExpMemoryLeak {

          public static void main(String[] args) throws Exception {
              NashornScriptEngine engine = (NashornScriptEngine) new NashornScriptEngineFactory()
                      .getScriptEngine("--class-cache-size=0");
              CompiledScript compiledScript = engine.compile("/.*/");
              Bindings bindings = engine.createBindings();
              compiledScript.eval(bindings);
              Object foo = new Object();
              bindings.put("foo", foo);

              WeakReference<Object> fooRef = new WeakReference<>(foo);
              foo = null;
              bindings = null;

              for (int i = 0; i < 10; i++) {
                  System.gc();
                  Thread.sleep(1000);
                  if (fooRef.get() == null) {
                      System.out.println("SUCCESS.");
                      return;
                  }
              }
              System.out.println("foo could not be GCed.");
          }
      }
      ---------- END SOURCE ----------

      FREQUENCY : always


        Attachments

          Activity

            People

            • Assignee:
              hannesw Hannes Wallnoefer
              Reporter:
              webbuggrp Webbug Group
            • Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated: