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

ConcurrentLinkedQueue memory leak

    Details

    • Type: Bug
    • Status: Resolved
    • Priority: P4
    • Resolution: Duplicate
    • Affects Version/s: 8u60
    • Fix Version/s: None
    • Component/s: core-libs
    • Labels:

      Description

      FULL PRODUCT VERSION :
      java version "1.8.0_60"
      Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
      Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)


      A DESCRIPTION OF THE PROBLEM :
      ConcurrentLinkedQueue leaks memory when removing the last element of a non-empty queue.

      This has been reported for the Cassandra project (see https://issues.apache.org/jira/browse/CASSANDRA-9549, the last comment on 15-Jul-2015), and in the Jetty project (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=477817)

      The problem is in method remove(Object), when the object last in the queue is the one to remove.
      What happens is that casItem() will succeed, but next = succ(p) will be null (since p is the last node), so the next "if" statement will not be entered and the last Node not unlinked.



      ADDITIONAL REGRESSION INFORMATION:
      AFAIK, this bug is present in the latest JDK 7, 8 and 9 releases.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run the test case below.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      No memory leaks.
      ACTUAL -
      Memory is leaked and when running with very small heaps (say 1 MiB), it takes about 50k iterations to exhaust the heap.

      Note also that because the last Node instance is never removed, more Nodes are appended and each further add/remove operation will have to navigate the linked list until the end with much CPU consumption and slowdown of performance of the methods.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      public class CLQBug
      {
          public static void main(String[] args)
          {
              Queue<Object> queue = new ConcurrentLinkedQueue<>();
              queue.offer(new Object());

              Object item = new Object();

              long iterations = 0;
              try
              {
                  while (true)
                  {
                      ++iterations;
                      queue.offer(item);
                      queue.remove(item);
                  }
              }
              catch (OutOfMemoryError e)
              {
                  queue = null;
                  System.err.println("iterations: " + iterations);
                  throw e;
              }
          }
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      No workaround, if not making sure that the object you are removing is not the last.

        Issue Links

          Activity

          Hide
          martin Martin Buchholz added a comment -
          Regression test for this:

          http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/test/jtreg/util/concurrent/ConcurrentLinkedQueue/RemoveLeak.java?view=markup

          Fix for this appears to be JSR166 CVS commit:

          revision 1.110
          date: 2015/01/16 22:38:54; author: jsr166; state: Exp; lines: +15 -10
          fix for 8054446: Repeated offer and remove on ConcurrentLinkedQueue lead to an OutOfMemoryError

           $ cvs diff -r 1.109 -r 1.110 ./main/java/util/concurrent/ConcurrentLinkedQueue.java
          Index: ./main/java/util/concurrent/ConcurrentLinkedQueue.java
          ===================================================================
          RCS file: /export/home/jsr166/jsr166/jsr166/src/main/java/util/concurrent/ConcurrentLinkedQueue.java,v
          retrieving revision 1.109
          retrieving revision 1.110
          diff -u -r1.109 -r1.110
          --- ./main/java/util/concurrent/ConcurrentLinkedQueue.java 4 Jan 2015 09:15:11 -0000 1.109
          +++ ./main/java/util/concurrent/ConcurrentLinkedQueue.java 16 Jan 2015 22:38:54 -0000 1.110
          @@ -447,18 +447,23 @@
                */
               public boolean remove(Object o) {
                   if (o != null) {
          - Node<E> pred = null;
          - for (Node<E> p = first(); p != null; p = succ(p)) {
          + Node<E> next, pred = null;
          + for (Node<E> p = first(); p != null; pred = p, p = next) {
          + boolean removed = false;
                           E item = p.item;
          - if (item != null &&
          - o.equals(item) &&
          - casItem(p, item, null)) {
          - Node<E> next = succ(p);
          - if (pred != null && next != null)
          - casNext(pred, p, next);
          - return true;
          + if (item != null) {
          + if (!o.equals(item)) {
          + next = succ(p);
          + continue;
          + }
          + removed = casItem(p, item, null);
                           }
          - pred = p;
          +
          + next = succ(p);
          + if (pred != null && next != null) // unlink
          + casNext(pred, p, next);
          + if (removed)
          + return true;
                       }
                   }
                   return false;
          Show
          martin Martin Buchholz added a comment - Regression test for this: http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/test/jtreg/util/concurrent/ConcurrentLinkedQueue/RemoveLeak.java?view=markup Fix for this appears to be JSR166 CVS commit: revision 1.110 date: 2015/01/16 22:38:54; author: jsr166; state: Exp; lines: +15 -10 fix for 8054446: Repeated offer and remove on ConcurrentLinkedQueue lead to an OutOfMemoryError  $ cvs diff -r 1.109 -r 1.110 ./main/java/util/concurrent/ConcurrentLinkedQueue.java Index: ./main/java/util/concurrent/ConcurrentLinkedQueue.java =================================================================== RCS file: /export/home/jsr166/jsr166/jsr166/src/main/java/util/concurrent/ConcurrentLinkedQueue.java,v retrieving revision 1.109 retrieving revision 1.110 diff -u -r1.109 -r1.110 --- ./main/java/util/concurrent/ConcurrentLinkedQueue.java 4 Jan 2015 09:15:11 -0000 1.109 +++ ./main/java/util/concurrent/ConcurrentLinkedQueue.java 16 Jan 2015 22:38:54 -0000 1.110 @@ -447,18 +447,23 @@       */      public boolean remove(Object o) {          if (o != null) { - Node<E> pred = null; - for (Node<E> p = first(); p != null; p = succ(p)) { + Node<E> next, pred = null; + for (Node<E> p = first(); p != null; pred = p, p = next) { + boolean removed = false;                  E item = p.item; - if (item != null && - o.equals(item) && - casItem(p, item, null)) { - Node<E> next = succ(p); - if (pred != null && next != null) - casNext(pred, p, next); - return true; + if (item != null) { + if (!o.equals(item)) { + next = succ(p); + continue; + } + removed = casItem(p, item, null);                  } - pred = p; + + next = succ(p); + if (pred != null && next != null) // unlink + casNext(pred, p, next); + if (removed) + return true;              }          }          return false;

            People

            • Assignee:
              martin Martin Buchholz
              Reporter:
              webbuggrp Webbug Group
            • Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: