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

System.gc() seems to eat memory rather than recycle it

XMLWordPrintable

    • gc
    • generic
    • generic



      Name: dkC59003 Date: 06/24/99



      The displayed below MemoryEater program tries to eat all the heap, and may explicitly invoke
      the method System.gc() just trying to improve memory utilization. Surprisingly, I have
      found that MemoryEater allocates 2.5-3 Mb of memory more if it avoids to invoke System.gc()
      method. This looks like explicit GC activation eats memory rather then helps to recycle it.

      I have tried MemoryEater for HotSpot releases 1.0-fcs, 1.3-betaH, and 2.0-devA, as well as
      for classic VM shipped with JDK releases 1.2, 1.2.1, 1.2.2U, and 1.3H, both with JIT on and
      off. All of those VMs suffer this problem, so I believe this is a bug in implementation of
      System.gc() method.

      Here are the results of MemoryEater execution under JDK 1.3-H on the following computers:
      - Pentium-II, 350MHz, 128Mb RAM, Windows NT 4.0 Workstation, IE 4.01
      - Sparc Ultra-1, 200MHz, 128Mb RAM, Solaris 2.7

      If System.gc() was invoked, HotSpot 1.3-betaH for Win32 results in:
          >>>>D:\jdk1.3H\bin\java MemoryEater invokeGC
          pool[3313874]=new Object(); // elapsed 2.654s
          pool[3313875]=new Object(); // elapsed 3.485s
          pool[3313876]=new Object(); // elapsed 2.403s
          pool[3313877]=new Object(); // elapsed 2.414s
          pool[3313878]=new Object(); // elapsed 2.413s
          pool[3313879]=new Object(); // elapsed 2.403s
          pool[3313880]=new Object(); // elapsed 2.414s
          pool[3313881]=new Object(); // elapsed 2.413s
          pool[3313882]=new Object(); // elapsed 2.404s
          pool[3313883]=new Object(); // elapsed 2.413s
          pool[3313884]=new Object(); // elapsed 2.404s
          pool[3313885]=new Object(); // elapsed 2.423s
          pool[3313886]=new Object(); // elapsed 2.404s
          pool[3313887]=new Object(); // elapsed 2.403s
          pool[3313888]=new Object(); // elapsed 2.414s
          pool[3313889]=new Object(); // elapsed 2.413s
          pool[3313890]=new Object(); // elapsed 2.414s
          pool[3313891]=new Object(); // elapsed 2.403s
          pool[3313892]=new Object(); // elapsed 2.404s
          pool[3313893]=new Object(); // elapsed 2.413s
          pool[3313894]=new Object(); // elapsed 2.413s
          pool[3313895]=new Object(); // elapsed 2.414s
          pool[3313896]=new Object(); // elapsed 2.423s
          pool[3313897]=new Object(); // elapsed 2.404s
          pool[3313898]=new Object(); // elapsed 2.423s
          OK: heap seems almost exhausted.

      And if System.gc() was not invoked, HotSpot 1.3-betaH for Win32 results in:
          >>>>D:\hotspot\jdk1.3H\bin\java MemoryEater
          pool[3313881]=new Object(); // elapsed 2.653s
          pool[3346962]=new Object(); // elapsed 1.051s
          pool[4133305]=new Object(); // elapsed 4.266s
          pool[4133306]=new Object(); // elapsed 65.495s
          OK: heap seems almost exhausted.

      So, explicit invocations of System.gc() prevented the HotSpot VM shipped with
      JDK 1.3-H for Win32 from allocating ~800000 extra instances of the class Object.
      (I guess, that each Object consumes at least 4 bytes to keep a reference to its
      Class instance, so that 800000 objects could occupy at least 3.2Mb in the heap.)

      Similarly, if System.gc() was invoked, libsunwjit.so shipped with JDK 1.3-H for
      Sparc results in:
          >>>> time $JAVA MemoryEater invokeGC
          pool[1053143]=new Object(); // elapsed 1.422s
          pool[1053144]=new Object(); // elapsed 1.373s
          pool[1053145]=new Object(); // elapsed 1.35s
          pool[1053146]=new Object(); // elapsed 1.345s
          pool[1053147]=new Object(); // elapsed 1.35s
          pool[1053148]=new Object(); // elapsed 1.349s
          pool[1053149]=new Object(); // elapsed 1.35s
          pool[1053150]=new Object(); // elapsed 1.347s
          pool[1053151]=new Object(); // elapsed 1.345s
          pool[1053152]=new Object(); // elapsed 1.35s
          pool[1053153]=new Object(); // elapsed 1.363s
          pool[1053154]=new Object(); // elapsed 1.344s
          pool[1053155]=new Object(); // elapsed 1.345s
          pool[1053156]=new Object(); // elapsed 1.349s
          pool[1053157]=new Object(); // elapsed 1.347s
          pool[1053158]=new Object(); // elapsed 1.346s
          pool[1053159]=new Object(); // elapsed 1.351s
          pool[1053160]=new Object(); // elapsed 1.378s
          pool[1053161]=new Object(); // elapsed 1.35s
          pool[1053162]=new Object(); // elapsed 1.346s
          pool[1053163]=new Object(); // elapsed 1.363s
          pool[1053164]=new Object(); // elapsed 1.347s
          pool[1053165]=new Object(); // elapsed 1.348s
          pool[1053166]=new Object(); // elapsed 1.348s
          pool[1053167]=new Object(); // elapsed 1.349s
          pool[1053168]=new Object(); // elapsed 1.345s
          pool[1053169]=new Object(); // elapsed 1.35s
          pool[1053170]=new Object(); // elapsed 1.348s
          pool[1053171]=new Object(); // elapsed 1.345s
          pool[1053172]=new Object(); // elapsed 1.349s
          pool[1053173]=new Object(); // elapsed 1.347s
          pool[1053174]=new Object(); // elapsed 1.37s
          pool[1053175]=new Object(); // elapsed 1.346s
          pool[1053176]=new Object(); // elapsed 1.346s
          pool[1053177]=new Object(); // elapsed 1.343s
          pool[1053178]=new Object(); // elapsed 1.347s
          pool[1053179]=new Object(); // elapsed 1.349s
          pool[1053180]=new Object(); // elapsed 1.343s
          pool[1053181]=new Object(); // elapsed 1.35s
          pool[1053182]=new Object(); // elapsed 1.349s
          pool[1053183]=new Object(); // elapsed 1.345s
          pool[1053184]=new Object(); // elapsed 1.344s
          pool[1053185]=new Object(); // elapsed 1.355s
          pool[1053186]=new Object(); // elapsed 1.367s
          pool[1053187]=new Object(); // elapsed 1.345s
          OK: heap seems almost exhausted.
          69.0u 0.0s 1:12 95% 0+0k 0+0io 0pf+0w

      And if System.gc() was not invoked, the same VM results in:
          >>>> time $JAVA MemoryEater
          pool[1053145]=new Object(); // elapsed 1.46s
          pool[1270989]=new Object(); // elapsed 1.326s
          pool[1577092]=new Object(); // elapsed 2.098s
          pool[1673356]=new Object(); // elapsed 1.269s
          pool[1674103]=new Object(); // elapsed 1.292s
          pool[1674109]=new Object(); // elapsed 1.267s
          pool[1674110]=new Object(); // elapsed 1.311s
          pool[1674111]=new Object(); // elapsed 1.296s
          pool[1674112]=new Object(); // elapsed 3.888s
          pool[1674113]=new Object(); // elapsed 1.314s
          pool[1674114]=new Object(); // elapsed 3.889s
          pool[1674115]=new Object(); // elapsed 3.869s
          pool[1674116]=new Object(); // elapsed 3.844s
          pool[1674117]=new Object(); // elapsed 3.902s
          pool[1674118]=new Object(); // elapsed 6.446s
          pool[1674119]=new Object(); // elapsed 9.192s
          OK: heap seems exhausted.
          106.0u 0.0s 1:48 97% 0+0k 0+0io 0pf+0w

      Again, explicit invocations of System.gc() prevented the classic VM 1.3-H for
      Sparc from allocating ~600000 extra instances of the class Object. (I.e.: method
      System.gc() has eaten ~2.5Mb in the heap, rather than improve memory utilization.)

      Results for other JDK releases are similar: classic VM looses ~2.5Mb of memory
      when invokes System.gc(), and HotSpot looses ~3Mb.

      Following is the source of MemoryEater class:

      /* @(#)MemoryEater.java 1.1 99/06/24
       * Copyright 99/06/24 Sun Microsystems, Inc.
       */

      /**
       * The class MemoryEater tries to allocate as many tiny instances of the type
       * <code>Object</code> as possible. Command-line parameters may either enforce
       * or do not enforce explicit invocation of garbage collector:
       * <br>&nbsp;&nbsp;
       * java MemoryEater [invokeGC]
       *
       * <p>When heap becomes almost exhausted, JVM beging to spend esential amounts of time
       * to allocate every new <code>Object</code>. If the parameter <code>invokeGC</code>
       * presents, MemoryEater invokes <code>System.gc()</code> when allocations speed slows
       * down in order to allow garbage collector to work better.
       *
       * <p>Surprisingly, I have found, that the class MemoryEater could allocate 600 to 800
       * thousands more of <code>Object</code> instances if <code>System.gc()</code> was not
       * explicitly invoked. If my guess is true, that every <code>Object</code> descriptor
       * consumes at least 4 bytes for reference to its class, then implicit invocations of
       * <code>System.gc()</code> prevented MemoryEater from allocating at least 2.4Mb to
       * 3.2Mb of extra memory.
       *
       * <p>I have observed this problem for the latest classic VMs released with JDK 1.2,
       * 1.2.1, 1.2.2, and 1.3, and for HotSpot releases 1.0-fcs both Win32 and Sparc, and
       * 1.3-betaI, and 2.0-devA for Win32. It looks like <code>System.gc()</code> consumes
       * too much of memory for its own needs, and this results in that GC seems to eat
       * memory rather than recycle it.
       *
       * <p>Note, that execution is terminated if memory allocation slows down crucially.
       * This is a workaround intended to avoid the HotSpot bug:
       * <br>&nbsp;&nbsp;
       * #4248801 (P1/S5) slow memory allocation when heap is almost exhausted
       *
       * @author Eugene I. Latkin
       */
      public class MemoryEater {
          /**
           * Parameter <code>args[]</code> may contain the keyword ``<code>invokeGC</code>''.
           */
          public static void main (String args[]) {
      // Parse parameter.
      boolean invokeGC = true;
      switch (args.length) {
      case 0:
      invokeGC = false;
      break;
      case 1:
      if (args[0].equals("invokeGC"))
      break;
      default:
      System.err.println("Illegal command-line parameter(s).");
      System.err.println("Execute:");
      System.err.println(" java MemoryEater invokeGC");
      System.err.println("or:");
      System.err.println(" java MemoryEater");
      return;
      };

      // Repository for objects, which should be allocated:
      Object pool[];
      int index=0;
      try {
      pool = new Object [1<<23]; // ~8 millions
      } catch (OutOfMemoryError oome) {
      System.err.println("Cannot initiate the test: need more memory.");
      return;
      };

      // Sum up time spent, when it was hard to JVM to allocate next object
      // (i.e.: when JVM has spent more than 1 second to allocate new object):
      double totalDelay = 0;
      long timeMark = System.currentTimeMillis();

      try {
      for (; index<pool.length; index++) {
      //-------------------------
      pool[index] = new Object();
      long nextTimeMark = System.currentTimeMillis();
      long elapsed = nextTimeMark - timeMark;
      timeMark = nextTimeMark;
      //----------------------

      if (elapsed > 1000) {
      double seconds = elapsed / 1000.0;
      System.out.println("pool[" + index + "]=new Object(); // elapsed "+ seconds + "s");

      totalDelay += seconds;
      if (totalDelay > 60) {
      System.out.println("OK: heap seems almost exhausted.");
      return;
      };

      if (invokeGC)
      System.gc();
      };
      };
      } catch (OutOfMemoryError oome) {
      pool = null;
      };
      System.out.println("OK: heap seems exhausted.");
          };
      }
          
      ======================================================================

            pbk Peter Kessler
            dkhukhrosunw Dmitry Khukhro (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: