JDK-8222941 : exception during ClassValue.remove permanently breaks the ClassValue
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 13
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2019-04-24
  • Updated: 2019-04-25
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Relates :  
Description
If ClassValueMap.removeEntry gets something like OutOfMemoryError here:

                // In an initialized state.  Bump forward, and de-initialize.
                classValue.bumpVersion();

then the ClassValue is permanently broken.  Future calls to remove() will do nothing, and computeValue will never be called again.  Instead, the value will be supplied from the cache.

% cat CV.java

import java.util.ArrayList;
import java.util.List;

public class CV {

    static int version = 0;
    static final ClassValue<String> resolvedJavaType = new ClassValue<>() {
      @Override
      protected String computeValue(Class<?> type) {
        return type.getName() + (++version);
      }
    };

    static void eatMemory(List<Object> list) {
        int size = 1000000;
        while (true) {
            try {
                list.add(new Object[size]);
            } catch (OutOfMemoryError oom) {
                size /= 2;
                if (size == 0) {
                    return;
                }
            }
        }
    }

    public static void main(String[] args) {
	var v1 = resolvedJavaType.get(CV.class);
	List<Object> storage = new ArrayList<Object>();
	boolean done = false;
	while (!done) {
	    eatMemory(storage);
	    try {
		resolvedJavaType.remove(CV.class);
	    } catch (Throwable t) {
		done = true;
		storage = null;
	    }
	}
	resolvedJavaType.remove(CV.class);
	var v2 = resolvedJavaType.get(CV.class);
	System.err.println("v1 " + v1);
	System.err.println("v2 " + v2);
	System.err.println("max version " + version);
	if (version != 2 || v1 == v2) {
	    throw new RuntimeException("Failed");
	}
    }
}

% javac CV.java
% java -Xmx64m CV
v1 CV1
v2 CV1
max version 1
Exception in thread "main" java.lang.RuntimeException: Failed
	at CV.main(CV.java:47)