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

Repeated offer and remove on ConcurrentLinkedQueue lead to an OutOfMemoryError

XMLWordPrintable

        FULL PRODUCT VERSION :
        tested on

        java version "1.8.0_05"
        Java(TM) SE Runtime Environment (build 1.8.0_05-b13)
        Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode)

        and

        java version "1.6.0_37"
        Java(TM) SE Runtime Environment (build 1.6.0_37-b06)
        Java HotSpot(TM) 64-Bit Server VM (build 20.12-b01, mixed mode)

        ADDITIONAL OS VERSION INFORMATION :
        independent of OS

        tested on ubuntu :
        Linux pc-cap90 3.11.0-24-generic #41-Ubuntu SMP Mon Jun 9 20:36:00 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

        EXTRA RELEVANT SYSTEM CONFIGURATION :
        to reproduce easily, small heap is helping (-Xmx4m)

        A DESCRIPTION OF THE PROBLEM :
        This test case with repeated offer and remove operations on ConcurrentLinkedQueue lead to an OutOfMemory Error.


        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :

        Run the following test :

            // Quick OutOfMemory with -Xmx4m
            public void testOutOfMemory(){
                ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<String>();
                String a="a";
                String b="b";
                queue.offer(a);
                for(int i=0;;i++){
                    if(i % 1024 == 0) {
                        System.out.println("i = "+i);
                    }
                    queue.offer(b);
                    queue.remove(b);

                }
            }


        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        No OutOfMemoryError.
        ACTUAL -
            i = 307200
            i = 308224
            i = 309248
            i = 310272

            java.lang.OutOfMemoryError: Java heap space
                           at java.util.concurrent.ConcurrentLinkedQueue.offer(ConcurrentLinkedQueue.java:274)
                           at TestConcurrentLinkedQueue16.testOutOfMemory



        REPRODUCIBILITY :
        This bug can be reproduced always.

        ---------- BEGIN SOURCE ----------
           public void testOutOfMemory(){
                ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<String>();
                String a="a";
                String b="b";
                queue.offer(a);
                for(int i=0;;i++){
                    if(i % 1024 == 0) {
                        System.out.println("i = "+i);
                    }
                    queue.offer(b);
                    queue.remove(b);

                }
            }
        ---------- END SOURCE ----------

        CUSTOMER SUBMITTED WORKAROUND :
        When called on the last element of a ConcurrentLinkedQueue with more than 1 element, the remove method nullify the item but the Node object is not unlinked from the list.
        After many add/remove the queue structure is : a -> null -> ... -> null -> b
        To be garbage collected the Node objects with null item must be unlinked from the list.
        They are unlinked only when the queue is iterated or when the head is polled.

        The advance() method of the Iterator contains code that skip and unlink nodes with null item :

                private E advance() {
                    lastRet = nextNode;
                    E x = nextItem;

                    Node<E> pred, p;
                    if (nextNode == null) {
                        p = first();
                        pred = null;
                    } else {
                        pred = nextNode;
                        p = succ(nextNode);
                    }

                    for (;;) {
                        if (p == null) {
                            nextNode = null;
                            nextItem = null;
                            return x;
                        }
                        E item = p.item;
                        if (item != null) {
                            nextNode = p;
                            nextItem = item;
                            return x;
                        } else {
                            // skip over nulls
                            Node<E> next = succ(p);
                            if (pred != null && next != null)
                                pred.casNext(p, next);
                            p = next;
                        }
                    }
                }

        A fix could be to unlink the Node objects with null items while iterating on the elements in the remove method.

              martin Martin Buchholz
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

                Created:
                Updated:
                Resolved: