jamm icon indicating copy to clipboard operation
jamm copied to clipboard

Weird behaviour (exception) measuring Collection<T> vs List<T>

Open marceloverdijk opened this issue 5 years ago • 6 comments

I'm trying to measure a Caffeine cache on JDK 10:

java version "10.0.2" 2018-07-17
Java(TM) SE Runtime Environment 18.3 (build 10.0.2+13)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.2+13, mixed mode)

The Caffeine cache object I have is Cache<String, Customer> where the Customer object only contains Strings, Longs and LocalDates. So nothing special.

When trying to meassure the cache object like: new MemoryMeter().measureDeep(cache) I get an exception:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.lang.String java.lang.module.ModuleDescriptor.name accessible: module java.base does not "opens java.lang.module" to unnamed module @3555913c] with root cause

java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.lang.String java.lang.module.ModuleDescriptor.name accessible: module java.base does not "opens java.lang.module" to unnamed module @3555913c
        at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:337) ~[na:na]
        at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:281) ~[na:na]
        at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:176) ~[na:na]
        at java.base/java.lang.reflect.Field.setAccessible(Field.java:170) ~[na:na]
        at org.github.jamm.MemoryMeter.addFieldChildren(MemoryMeter.java:330) ~[jamm-0.3.2.jar:na]
        at org.github.jamm.MemoryMeter.measureDeep(MemoryMeter.java:269) ~[jamm-0.3.2.jar:na]

I did some further investigation and by measuring on a different objects derived from the cache instance instead.

// get a collection of the objects in the cache.
Collection<Customer> customerCollection = cache.asMap().values();

// create a new list containing the objects in the collection.
List<Customer> customerList = new ArrayList<>();
customerCollection.forEach(c -> customerList.add(c));

// now measure.
memoryMeter().measureDeep(customerList); // succeeds
memoryMeter().measureDeep(customerCollection); // fails with same exception

The customerCollection returned by the Caffeine cache is a com.github.benmanes.caffeine.cache.BoundedLocalCache$ValuesView (extending java.util.AbstractCollection) so probably JAMM is not able to meassure this ValuesView

marceloverdijk avatar Aug 27 '18 12:08 marceloverdijk

Maybe @ben-manes can also shed some light on this..

marceloverdijk avatar Aug 27 '18 12:08 marceloverdijk

Are you using modules or classpath?

mebigfatguy avatar Aug 27 '18 16:08 mebigfatguy

I have a Spring Boot application running as a jar (embedded Tomcat). I haven't done anything with modules, but it is Java 10 application.

marceloverdijk avatar Aug 27 '18 19:08 marceloverdijk

I just got hit by this exact exception but in a different context, instead operating on a com.sun.imageio.plugins.jpeg.JPEGImageReader object.

(mm/measure
 ((fn dimensions [src]
    ;; Use io/as-file rather than assuming `src` is a resource
    (let [image-input-stream (ImageIO/createImageInputStream
                              (io/as-file src))
          readers (ImageIO/getImageReaders image-input-stream)]
      (let [r (.next readers)]
        (.setInput r image-input-stream)
        [(.getWidth r 0) (.getHeight r 0)]
        r)))
  "/Users/timothyvisher/wallpaperrr/library/1000x1152/Typographic Verses (15)_1000x1152.jpeg"))
1. Unhandled java.lang.reflect.InaccessibleObjectException
   Unable to make field private
   jdk.internal.reflect.ConstructorAccessorImpl
   jdk.internal.reflect.DelegatingConstructorAccessorImpl.delegate
   accessible: module java.base does not "opens jdk.internal.reflect" to
   unnamed module @1be4a87b

     AccessibleObject.java:  340  java.lang.reflect.AccessibleObject/checkCanSetAccessible
     AccessibleObject.java:  280  java.lang.reflect.AccessibleObject/checkCanSetAccessible
                Field.java:  176  java.lang.reflect.Field/checkCanSetAccessible
                Field.java:  170  java.lang.reflect.Field/setAccessible
          MemoryMeter.java:  330  org.github.jamm.MemoryMeter/addFieldChildren
          MemoryMeter.java:  269  org.github.jamm.MemoryMeter/measureDeep
NativeMethodAccessorImpl.java:   -2  jdk.internal.reflect.NativeMethodAccessorImpl/invoke0
NativeMethodAccessorImpl.java:   62  jdk.internal.reflect.NativeMethodAccessorImpl/invoke
DelegatingMethodAccessorImpl.java:   43  jdk.internal.reflect.DelegatingMethodAccessorImpl/invoke
               Method.java:  566  java.lang.reflect.Method/invoke
user> (System/getProperty "java.version")
11.0.1

timvisher avatar Jun 28 '19 13:06 timvisher

The problem seems to be caused by the Java Platform Module System introduced in Java 9. https://stackoverflow.com/questions/41265266/how-to-solve-inaccessibleobjectexception-unable-to-make-member-accessible-m

blerer avatar Jul 01 '19 07:07 blerer

Indeed, these are "classic" Jigsaw-induced exceptions. The changes around enforcing the security model started off as warnings, but newer versions of Java have promoted them to hard errors unless you add specific flags to disable the checks.

The first example should be fixable by either explicitly "modularizing" and adding the correct "opens" declaration to module-info.java, or by setting the flags in question. The second one could prove rather more "interesting", as things in jdk.internal are pretty much never going to be made accessible to "the outside world".

The only "fixes" for this which I can think of offhand (other than adding the flags) would be for JAMM to "probe ahead" and check whether it will be permitted to open something, and stop "going deeper" when it hits such things, or to leverage some combination of the debugging and/or agent APIs to obtain the information.

Not actually sure it is even available there, but it certainly isn't (and won't be) available through the JVM "directly" without using the flags to forcibly change JVM behavior. The new security model is only going to become more pervasive over time — actually, in all likelihood this would have broken before if you were using sealed packages. All that has really changed is that now everything is approximately "sealed by default".

Fentonator avatar Aug 22 '19 23:08 Fentonator

Thanks for reporting the problem. We have addressed the problems linked to the Java Module System.

blerer avatar May 23 '23 08:05 blerer