hibernate/hibernate-orm · error · IllegalArgumentException

Maximum capacity has to be at least twice the concurrencyLev

Error message

Maximum capacity has to be at least twice the concurrencyLevel

What it means

BoundedConcurrentHashMap is Hibernate's LIRS-backed bounded map (used for the query plan cache). Its segments need at least two slots each, so the constructor first clamps concurrencyLevel to capacity/2 (min 1) and then rejects the pair when capacity < concurrencyLevel << 1 (except the special case capacity == 1). This IllegalArgumentException means the requested capacity cannot host the requested segment count.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/internal/util/collections/BoundedConcurrentHashMap.java:1461

	 * internal sizing to try to accommodate this many threads.
	 * @param evictionStrategy the algorithm used to evict elements from this map
	 *
	 * @throws IllegalArgumentException if the initial capacity is negative or the load factor or concurrencyLevel are
	 * nonpositive.
	 */
	public BoundedConcurrentHashMap(
			int capacity, int concurrencyLevel,
			Eviction evictionStrategy) {
		if ( capacity < 0 || concurrencyLevel <= 0 ) {
			throw new IllegalArgumentException();
		}

		concurrencyLevel = Math.min( capacity / 2, concurrencyLevel ); // concurrencyLevel cannot be > capacity/2
		concurrencyLevel = Math.max( concurrencyLevel, 1 ); // concurrencyLevel cannot be less than 1

		// minimum two elements per segment
		if ( capacity < concurrencyLevel << 1 && capacity != 1 ) {
			throw new IllegalArgumentException( "Maximum capacity has to be at least twice the concurrencyLevel" );
		}

		if ( evictionStrategy == null ) {
			throw new IllegalArgumentException();
		}

		if ( concurrencyLevel > MAX_SEGMENTS ) {
			concurrencyLevel = MAX_SEGMENTS;
		}

		// Find power-of-two sizes best matching arguments
		int sshift = 0;
		int ssize = 1;
		while ( ssize < concurrencyLevel ) {
			++sshift;
			ssize <<= 1;
		}
		segmentShift = 32 - sshift;

View on GitHub (pinned to fad1729dce)

Solutions

  1. Raise capacity to at least 2 * concurrencyLevel (power-of-two capacity is ideal)
  2. Or lower concurrencyLevel — for small maps 1 or 2 segments are plenty
  3. Check the cache settings that feed these numbers (e.g. plan cache sizing) and recompute them together, not independently

Example fix

// before: 8 slots cannot host 16 segments
Map<K, V> cache = new BoundedConcurrentHashMap<>( 8, 16, Eviction.LIRS );
// after: capacity at least twice the concurrency level
Map<K, V> cache = new BoundedConcurrentHashMap<>( 32, 16, Eviction.LIRS );
Defensive patterns

Strategy: validation

Validate before calling

// Validate before constructing a bounded concurrent map
static <K, V> BoundedConcurrentHashMap<K, V> boundedMap(int capacity, int concurrencyLevel, Eviction eviction) {
    if ( capacity < 0 ) throw new IllegalArgumentException("capacity must be >= 0");
    concurrencyLevel = Math.min( Math.max( Math.min( capacity / 2, concurrencyLevel ), 1 ), capacity == 1 ? 1 : capacity / 2 );
    if ( capacity != 1 && capacity < concurrencyLevel << 1 ) {
        concurrencyLevel = Math.max( 1, capacity / 2 ); // clamp instead of throwing
    }
    return new BoundedConcurrentHashMap<>( capacity, concurrencyLevel, eviction );
}

Prevention

When it happens

Trigger: new BoundedConcurrentHashMap<>(capacity, concurrencyLevel, evictionStrategy) with capacity smaller than twice concurrencyLevel, e.g. (8, 16), (3, 2) — note 3 < 2*2 and 3 != 1, so it throws; (2, 1) passes after clamping.

Common situations: Shrinking Hibernate caches (query plan cache sizing derived from hibernate.query.plan_cache_max_size) or other tuned caches to very small capacities while keeping a default concurrency level of 16; porting constructor arguments from ConcurrentHashMap, which allows any combination.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/1424661359d66b0c. Report an issue: GitHub.