jamm
jamm copied to clipboard
Weird behaviour (exception) measuring Collection<T> vs List<T>
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 String
s, Long
s 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
Maybe @ben-manes can also shed some light on this..
Are you using modules or classpath?
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.
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
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
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".
Thanks for reporting the problem. We have addressed the problems linked to the Java Module System.