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

LinkedBlockingDeque offer() creates nodes even if capacity has been reached

XMLWordPrintable

      ADDITIONAL SYSTEM INFORMATION :
      All versions

      A DESCRIPTION OF THE PROBLEM :
      In the JavaDoc of LinkedBlockingDeque, it states: "Linked nodes are dynamically created upon each insertion unless this would bring the deque above capacity." However, in the current implementation, nodes are always created, even if the deque is full. This is because count is non-volatile, and we only check inside the linkFirst/Last() methods whether the queue is full. At this point we have already locked and have created the Node. Instead, the count could be volatile, and we could check before locking.

      In the current version, calling offer() on a full LinkedBlockingDeque creates unnecessary objects and contention. Similarly for poll() and peek(), we could exit prior to locking by checking the count field.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run the test code below with -verbose:gc

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      We would expect minimal object creation if the LBD adhered to the JavaDoc. Each of the runs should be in the tens of milliseconds.
      ACTUAL -
      Instead, for the LinkedBlockingDeque, the runs take several seconds for offer and poll, for example:

      [0.003s][info][gc] Using G1
      openjdk version "24" 2025-03-18
      OpenJDK Runtime Environment Corretto-24.0.0.36.2 (build 24+36-FR)
      OpenJDK 64-Bit Server VM Corretto-24.0.0.36.2 (build 24+36-FR, mixed mode, sharing)
      LinkedBlockingQueue
      q = [42]
      offerTime = 85ms
      q = []
      pollTime = 26ms
      LinkedBlockingDeque
      q = [42]
      offerTime = 1516ms
      q = []
      pollTime = 1509ms
      LinkedBlockingQueue
      q = [42]
      offerTime = 13ms
      q = []
      pollTime = 22ms
      LinkedBlockingDeque
      [3.245s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 65M->4M(1552M) 1.726ms
      [3.950s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 692M->4M(1552M) 1.368ms
      [4.592s][info][gc] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 740M->4M(1552M) 1.415ms
      [5.436s][info][gc] GC(3) Pause Young (Normal) (G1 Evacuation Pause) 868M->4M(1552M) 1.290ms
      q = [42]
      offerTime = 2285ms
      q = []
      pollTime = 1492ms
      LinkedBlockingQueue
      q = [42]
      offerTime = 26ms
      q = []
      pollTime = 22ms
      LinkedBlockingDeque
      [7.751s][info][gc] GC(4) Pause Young (Normal) (G1 Evacuation Pause) 900M->4M(1552M) 1.386ms
      [8.540s][info][gc] GC(5) Pause Young (Normal) (G1 Evacuation Pause) 900M->4M(1552M) 1.351ms
      q = [42]
      offerTime = 2065ms
      q = []
      pollTime = 1593ms

      ---------- BEGIN SOURCE ----------
      import java.util.*;
      import java.util.concurrent.*;
      import java.util.stream.*;

      public class LotsOfOffersOnFullDeque {
          public static void main(String... args) {
              for (int i = 0; i < 3; i++) {
                  testOfferPoll(new LinkedBlockingQueue<>(1)); // 30ms for both
                  testOfferPoll(new LinkedBlockingDeque<>(1)); // 2s, 1.4s
              }
          }

          private static void testOfferPoll(Queue<Integer> q) {
              System.out.println(q.getClass().getSimpleName());
              long offerTime = System.nanoTime();
              try {
                  IntStream.range(0, 100_000_000)
                          .parallel()
                          .forEach(i -> q.offer(42));
                  System.out.println("q = " + q);
              } finally {
                  offerTime = System.nanoTime() - offerTime;
                  System.out.printf("offerTime = %dms%n", (offerTime / 1_000_000));
              }

              long pollTime = System.nanoTime();
              try {
                  IntStream.range(0, 100_000_000)
                          .parallel()
                          .forEach(i -> q.poll());
                  System.out.println("q = " + q);
              } finally {
                  pollTime = System.nanoTime() - pollTime;
                  System.out.printf("pollTime = %dms%n", (pollTime / 1_000_000));
              }
          }
      }

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

            Unassigned Unassigned
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: