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

Table backing ConcurrentHashMap created with unexpected "initialCapacity".

    Details

      Description

      ADDITIONAL SYSTEM INFORMATION :
      OS: ArcoLinuxB-openbox v18.12.7 (based on Arch Linux)
      Kernel: 5.4.5-arch1-1
      Java:
      openjdk version "13.0.1" 2019-10-15
      OpenJDK Runtime Environment (build 13.0.1+9)
      OpenJDK 64-Bit Server VM (build 13.0.1+9, mixed mode)

      A DESCRIPTION OF THE PROBLEM :
      The internal backing table is created with unexpected "initialCapacity" when passing that through contructors of ConcurrentHashMap.
      The "loadFactor" parameter influences the aforementioned "initialCapacity" too, besides that's not respected (it is always used a "loadFactor" of 0.75) and its semantic is unconsistent (we can use values with arbitrary values above 1.0 obtaining a decreasing "initialCapacity").

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      To reproduce uncomment one example line at a time in the below code.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The expected result should be an initial table length equal to the passed "initialCapacity" or, at most, equal to the closer (greater) power of two.
      ACTUAL -
      The actual result is written in the comment at the end of every example line.

      ---------- BEGIN SOURCE ----------
      import java.lang.reflect.Field;
      import java.util.concurrent.ConcurrentHashMap;

      public class BugRep {

      public static void main(String[] args) throws Exception {

      ConcurrentHashMap<String, String> m;

      //Uncomment one line at a time to get the result for each example input
      m = new ConcurrentHashMap<String, String>(15); //Expected: 16 - Actual: 32
      //m = new ConcurrentHashMap<String, String>(15, 0.94f); //Expected: 16 - Actual: 16
      //m = new ConcurrentHashMap<String, String>(16); //Expected: 16 - Actual: 32
      //m = new ConcurrentHashMap<String, String>(16, 0.1f); //Expected: 16 - Actual: 256
      //m = new ConcurrentHashMap<String, String>(16, 0.75f); //Expected: 16 - Actual: 32
      //Note the "loadFactor" greater than '1.0' in the next two lines
      //m = new ConcurrentHashMap<String, String>(16, 1.1f); //Expected: 16 - Actual: 16
      //m = new ConcurrentHashMap<String, String>(16, 9.0f); //Expected: 16 - Actual: 2

      /*
      * The backing table is actually created after putting the first value in that.
      * Changing the max value of 'i' you can verify that the "loadFactor" is always 0.75
      * also if a different value has been passed to the constructor. Besides the resizing
      * of the table is made when the number of entries hit the product of the load factor
      * and the current capacity, not when exceeds it like in HashMap (verified).
      */
      for (int i = 1; i <= 1; i++)
      m.put(Integer.toString(i), "");

      //Access to the backing "table" through reflection
      Field tableField = ConcurrentHashMap.class.getDeclaredField("table");
      tableField.setAccessible(true);
      Object[] table = (Object[]) tableField.get(m);
      System.out.print("The table length is: ");
      System.out.println(table == null ? 0 : table.length);
      }
      }

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

      FREQUENCY : always


        Attachments

          Activity

            People

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

              Dates

              • Created:
                Updated: