Re: Questions about JDK-8136353 "ClassValue preventing class unloading"

2016-01-08 Thread Peter Levart



On 01/07/2016 02:18 AM, William ML Leslie wrote:

On 7 January 2016 at 07:34, Peter Levart  wrote:

ClassValue API could use a different strategy and reference the cached
instance of type T from ClassValue instance instead, but then you would
have a problem with ClassValue instances reachable from other class loaders
than the cached instances of type T.

Regards, Peter

Is the problem with this strategy [together with a
java.util.WeakHashMap] that without this strong reference to the Class
object, the Class itself could be collected?



Hi William,

ClassValue API maps a pair (Class, ClassValue) -> cached instance. The 
cached instance is "retrievable" if and only if both parts of the pair 
(Class, ClassValue) can be produced. As soon as either Class key or 
ClassValue instance becomes unreachable, the cached instance could 
become unreachable too as it is clearly not "retrievable" any more. But 
the cached instance must be kept reachable while it is still possible to 
produce both parts of the pair (Class, ClassValue). So ClassValue API 
chooses to make a strong link from Class key to cached instance. 
Alternative strategy would be for API to make a strong link from 
ClassValue instance to cached instance. Either case has it's drawback.


The current ClassValue strategy has drawback when the cached instance 
directly or indirectly references the ClassValue instance it was 
constructed in (the case of the failing test in this thread). The cached 
instance lifetime is then bound to the lifetime of the Class key.


The alternative strategy has drawback when the cached instance directly 
of indirectly references the Class key it was constructed for. The 
cached instance lifetime is then bound to the lifetime of the ClassValue 
instance.


There's no way out short of choosing the right strategy for the task at 
hand. And I showed in the last post how to effectively transform current 
strategy to the alternative strategy by introducing an indirection to 
the cached value through a WeakReference and making a strong link from 
ClassValue instance to cache value at the same time.


Regards, Peter

P.S. With Ephemerons, I think the general solution would be possible.

___
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev


Re: Questions about JDK-8136353 "ClassValue preventing class unloading"

2016-01-08 Thread Peter Levart

Hi Jochen,

On 01/07/2016 03:05 PM, Jochen Theodorou wrote:

Hi Peter,

Am 06.01.2016 um 21:34 schrieb Peter Levart:
[...]


ClassValue API could use a different strategy and reference the cached
instance of type T from ClassValue instance instead, but then you
would have a problem with ClassValue instances reachable from other
class loaders than the cached instances of type T.


yes, that is what I tried as well... I used my own map directly in 
ClassValue and avoided the map ClassValue uses natively.


So the solution for this case is basically using a Soft- or 
Weak-Reference in Dummy, right?


The solution to use any Class key and not be dependent on it's origin 
(i.e. the class loader) and therefore on it's lifetime, is to decouple 
the cached value from the Class key by an indirection through a 
WeakReference, but then you should make sure the cached value doesn't go 
away while it is still possible to retrieve it - the trick is to hook it 
on a list that is reachable from the ClassValue instance that 
constructed it:


import java.io.File;
import java.io.UncheckedIOException;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CVTest {

public static class MyClassValue extends 
ClassValue> {

private final List dummies = new ArrayList<>();
@Override
protected WeakReference computeValue(Class type) {
Dummy ret = new Dummy();
// make it reachable from MyClassValue instance that 
constructed it

synchronized(dummies) {
dummies.add(ret);
}

Dummy.o = this;

// decouple it from 'type' - make it only weakly reachable 
from 'type'

return new WeakReference<>(ret);
}
}

public static class Dummy {
static Object o;

@Override
protected void finalize() throws Throwable {
System.out.println(this + " finalized");
}
}

public static void main(String[] args) throws Exception {
String[] cp = System.getProperty("java.class.path")
.split(Pattern.quote(File.pathSeparator));
URL[] urls = Stream.of(cp).map(f -> {
try {
return Paths.get(f).toUri().toURL();
} catch (MalformedURLException e) {
throw new UncheckedIOException(e);
}
}).collect(Collectors.toList()).toArray(new URL[0]);

for (long i = 0; i < 10; i++) {
URLClassLoader classLoader = new URLClassLoader(
urls, ClassLoader.getSystemClassLoader().getParent());
@SuppressWarnings("unchecked")
ClassValue> cv = 
(ClassValue>)

classLoader.loadClass("CVTest$MyClassValue").newInstance();

WeakReference valueRef = cv.get(Integer.TYPE);

// while 'cv' is alive, 'value' is alive too since 'cv' keeps a
// strong reference to 'value' in its 'dummies' list...
Object value = valueRef.get();

assert value != null;
assert value.getClass().getClassLoader() == classLoader;
System.out.println(i);
classLoader.close();
classLoader = null;
cv = null;
value = null;
System.gc();
Thread.sleep(200L);
}
}
}


...running this example produces:

0
CVTest$Dummy@6b027549 finalized
1
CVTest$Dummy@65c59659 finalized
2
CVTest$Dummy@61fa1e39 finalized
...
...


Regards, Peter




bye Jochen
___
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev


___
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev


Re: Questions about JDK-8136353 "ClassValue preventing class unloading"

2016-01-07 Thread Peter Levart



On 01/07/2016 03:05 PM, Jochen Theodorou wrote:

Hi Peter,

Am 06.01.2016 um 21:34 schrieb Peter Levart:
[...]

Currently the cached instance of type T is reachable from Class
instance and you are using Integer.TYPE in your example. Which means
that the cached instance of T is never going to be released if
ClassValue instance is reachable from cached instance of T. In your
example, it is: (Dummy instance -> Dummy.class -> MyClassValue
instance). So you get this reachability chain: Integer.TYPE ->
ClassValueMap instance -> ClassValueMap.Entry[] -> ClassValueMap.Entry
-> Dummy instance -> Dummy.class -> MyClassValue instance.

>

Integer.TYPE is a Class instance representing int type which is
loaded by bootstrap class loader, which never goes away.


So in other words, this is supposed to work if I used for example 
Dummy instead of Integer.TYPE? Because this is not the case (just 
tested with 1.8.0_66). Which means we are not talking only about 
bootstrap classes, but about basically any class.


Hi Jochen,

I suspect you specified MyClassValue.Dummy.class instead of Integer.TYPE 
? Was Dummy.class loaded by same class loader as the MyClassValue for 
which the ClassValue.get() was executed. I have tried to execute the 
following variation of the test (no need to pack a separate jar file - 
you can execute if directly from within an IDE):


import java.io.File;
import java.io.UncheckedIOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CVTest {

public static class MyClassValue extends ClassValue {
protected Object computeValue(Class type) {
Dummy ret = new Dummy();
Dummy.o = this;
return ret;
}
}

public static class Dummy {
static Object o;

@Override
protected void finalize() throws Throwable {
System.out.println(this + " finalized");
}
}

public static void main(String[] args) throws Exception {
String[] cp = System.getProperty("java.class.path")
.split(Pattern.quote(File.pathSeparator));
URL[] urls = Stream.of(cp).map(f -> {
try {
return Paths.get(f).toUri().toURL();
} catch (MalformedURLException e) {
throw new UncheckedIOException(e);
}
}).collect(Collectors.toList()).toArray(new URL[0]);

for (long i = 0; i < 10; i++) {
URLClassLoader classLoader = new URLClassLoader(
urls, ClassLoader.getSystemClassLoader().getParent());
ClassValue cv = (ClassValue)
classLoader.loadClass("CVTest$MyClassValue").newInstance();
Class key = classLoader.loadClass("CVTest$Dummy");
Object value = cv.get(key);
assert value.getClass().getClassLoader() == classLoader;
assert key.getClassLoader() == classLoader;
System.out.println(i);
classLoader.close();
classLoader = null;
cv = null;
key = null;
value = null;
System.gc();
Thread.sleep(200L);
}
}
}


...and it prints:

0
CVTest$Dummy@6b027549 finalized
1
CVTest$Dummy@65c59659 finalized
2
CVTest$Dummy@61fa1e39 finalized
3
CVTest$Dummy@24f89c62 finalized
4
CVTest$Dummy@5ca80d89 finalized
5
CVTest$Dummy@15963f1b finalized
6
CVTest$Dummy@242cf046 finalized
7
CVTest$Dummy@44d7632d finalized
8
CVTest$Dummy@588ffa7b finalized
9
CVTest$Dummy@7cc57896 finalized


...indicating that objects do get released in this case.


Regards, Peter




ClassValue API could use a different strategy and reference the cached
instance of type T from ClassValue instance instead, but then you
would have a problem with ClassValue instances reachable from other
class loaders than the cached instances of type T.


yes, that is what I tried as well... I used my own map directly in 
ClassValue and avoided the map ClassValue uses natively.


So the solution for this case is basically using a Soft- or 
Weak-Reference in Dummy, right?


bye Jochen
___
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev


___
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev


Re: Questions about JDK-8136353 "ClassValue preventing class unloading"

2016-01-07 Thread Jochen Theodorou

Hi Peter,

Am 06.01.2016 um 21:34 schrieb Peter Levart:
[...]

Currently the cached instance of type T is reachable from Class
instance and you are using Integer.TYPE in your example. Which means
that the cached instance of T is never going to be released if
ClassValue instance is reachable from cached instance of T. In your
example, it is: (Dummy instance -> Dummy.class -> MyClassValue
instance). So you get this reachability chain: Integer.TYPE ->
ClassValueMap instance -> ClassValueMap.Entry[] -> ClassValueMap.Entry
-> Dummy instance -> Dummy.class -> MyClassValue instance.

>

Integer.TYPE is a Class instance representing int type which is
loaded by bootstrap class loader, which never goes away.


So in other words, this is supposed to work if I used for example Dummy 
instead of Integer.TYPE? Because this is not the case (just tested with 
1.8.0_66). Which means we are not talking only about bootstrap classes, 
but about basically any class.



ClassValue API could use a different strategy and reference the cached
instance of type T from ClassValue instance instead, but then you
would have a problem with ClassValue instances reachable from other
class loaders than the cached instances of type T.


yes, that is what I tried as well... I used my own map directly in 
ClassValue and avoided the map ClassValue uses natively.


So the solution for this case is basically using a Soft- or 
Weak-Reference in Dummy, right?


bye Jochen
___
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev


Re: Questions about JDK-8136353 "ClassValue preventing class unloading"

2016-01-06 Thread William ML Leslie
On 7 January 2016 at 07:34, Peter Levart  wrote:
> ClassValue API could use a different strategy and reference the cached
> instance of type T from ClassValue instance instead, but then you would
> have a problem with ClassValue instances reachable from other class loaders
> than the cached instances of type T.
>
> Regards, Peter

Is the problem with this strategy [together with a
java.util.WeakHashMap] that without this strong reference to the Class
object, the Class itself could be collected?

-- 
William Leslie

Notice:
Likely much of this email is, by the nature of copyright, covered
under copyright law.  You absolutely MAY reproduce any part of it in
accordance with the copyright law of the nation you are reading this
in.  Any attempt to DENY YOU THOSE RIGHTS would be illegal without
prior contractual agreement.
___
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev


Re: Questions about JDK-8136353 "ClassValue preventing class unloading"

2016-01-06 Thread Peter Levart



On 01/06/2016 09:34 PM, Peter Levart wrote:

Hi Craig,

I think that what you are asking for is impossible to achieve in 
current Java.


ClassValue API is basically a cache that maps a pair: (ClassValue, 
Class) -> cached instance of type T


You would like the cached instance of type T to not be reachable from 
either ClassValue instance or from Class instance, but still be 
non-reclaimable while either of 

correction: while either of ... or ... is -> while both of ... and ... are

ClassValue instance or Class instance is reachable in some other 
way that excludes being reachable from the cached instance of type T.


For that to be achievable, Java would have to provide a feature called 
"Ephemeron".


Currently the cached instance of type T is reachable from Class 
instance and you are using Integer.TYPE in your example. Which means 
that the cached instance of T is never going to be released if 
ClassValue instance is reachable from cached instance of T. In your 
example, it is: (Dummy instance -> Dummy.class -> MyClassValue 
instance). So you get this reachability chain: Integer.TYPE -> 
ClassValueMap instance -> ClassValueMap.Entry[] -> ClassValueMap.Entry 
-> Dummy instance -> Dummy.class -> MyClassValue instance.
Integer.TYPE is a Class instance representing int type which is 
loaded by bootstrap class loader, which never goes away.


ClassValue API could use a different strategy and reference the cached 
instance of type T from ClassValue instance instead, but then you 
would have a problem with ClassValue instances reachable from other 
class loaders than the cached instances of type T.


Regards, Peter

On 01/06/2016 06:14 PM, Craig Andrews wrote:
I apologize for contacting this list - I'm sure this isn't the "right 
way" to contact the OpenJDK project, but I'm not clear what the 
"right way" is.


I'm hoping to raise https://bugs.openjdk.java.net/browse/JDK-8136353 
"ClassValue preventing class unloading" as I believe it's a 
significant issue in ClassValue which is a blocker for its use in 
dynamic languages (which is primiarly why ClassValue was introduced). 
I think the P5 priority set on the bug now is way too low, perhaps 
you could consider raising the priority?


The source code in the issue description is incorrect; it doesn't 
compile. Could you please attach the working test cases to the issue, 
so future testers and developers can reproduce the problem? Here are 
links to the 2 source code files:

https://issues.apache.org/jira/secure/attachment/12765900/CVTest.java
https://issues.apache.org/jira/secure/attachment/12765901/MyClassValue.java 

(which of course should be directly attached to the openjdk issue 
tracker issue, and not hyperlinked to the Groovy tracker)


And to reproduce the issue, run:
$ javac MyClassValue.java && javac CVTest.java && mkdir -p t && jar 
cvf t/t.jar MyClassValue*.class && rm MyClassValue*.class && 
JAVA_OPTS=-Xmx4m java CVTest

and wait for the an error to occur, which is:
Exception in thread "main" java.lang.OutOfMemoryError: Compressed 
class space

at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:759)
at 
java.security.SecureClassLoader.defineClass(SecureClassLoader.java:152)

at java.net.URLClassLoader.defineClass(URLClassLoader.java:470)
at java.net.URLClassLoader.access$100(URLClassLoader.java:76)
at java.net.URLClassLoader$1.run(URLClassLoader.java:371)
at java.net.URLClassLoader$1.run(URLClassLoader.java:365)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:364)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
at CVTest.main(CVTest.java:12)

The bug is reproducible on all JDK 8 and 9 builds (I tested up to JDK 
9 build 99).


Based on my understanding of the situation from my research in the 
Groovy bug that discovered the issue ( 
https://issues.apache.org/jira/browse/GROOVY-6704 ) and some work 
another person did with the YourKit profiler ( 
https://www.yourkit.com/forum/viewtopic.php?f=3&t=12658 ), I suspect 
that the fix for https://bugs.openjdk.java.net/browse/JDK-8032606 
"ClassValue.ClassValueMap.type is unused" is relevant; I think the 
problem lies in the java.lang.Class.classValueMap implementation.


Thank you,
~Craig Andrews
___
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev




___
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev


Re: Questions about JDK-8136353 "ClassValue preventing class unloading"

2016-01-06 Thread Peter Levart

Hi Craig,

I think that what you are asking for is impossible to achieve in current 
Java.


ClassValue API is basically a cache that maps a pair: (ClassValue, 
Class) -> cached instance of type T


You would like the cached instance of type T to not be reachable from 
either ClassValue instance or from Class instance, but still be 
non-reclaimable while either of ClassValue instance or Class 
instance is reachable in some other way that excludes being reachable 
from the cached instance of type T.


For that to be achievable, Java would have to provide a feature called 
"Ephemeron".


Currently the cached instance of type T is reachable from Class 
instance and you are using Integer.TYPE in your example. Which means 
that the cached instance of T is never going to be released if 
ClassValue instance is reachable from cached instance of T. In your 
example, it is: (Dummy instance -> Dummy.class -> MyClassValue 
instance). So you get this reachability chain: Integer.TYPE -> 
ClassValueMap instance -> ClassValueMap.Entry[] -> ClassValueMap.Entry 
-> Dummy instance -> Dummy.class -> MyClassValue instance.
Integer.TYPE is a Class instance representing int type which is 
loaded by bootstrap class loader, which never goes away.


ClassValue API could use a different strategy and reference the cached 
instance of type T from ClassValue instance instead, but then you 
would have a problem with ClassValue instances reachable from other 
class loaders than the cached instances of type T.


Regards, Peter

On 01/06/2016 06:14 PM, Craig Andrews wrote:
I apologize for contacting this list - I'm sure this isn't the "right 
way" to contact the OpenJDK project, but I'm not clear what the "right 
way" is.


I'm hoping to raise https://bugs.openjdk.java.net/browse/JDK-8136353 
"ClassValue preventing class unloading" as I believe it's a 
significant issue in ClassValue which is a blocker for its use in 
dynamic languages (which is primiarly why ClassValue was introduced). 
I think the P5 priority set on the bug now is way too low, perhaps you 
could consider raising the priority?


The source code in the issue description is incorrect; it doesn't 
compile. Could you please attach the working test cases to the issue, 
so future testers and developers can reproduce the problem? Here are 
links to the 2 source code files:

https://issues.apache.org/jira/secure/attachment/12765900/CVTest.java
https://issues.apache.org/jira/secure/attachment/12765901/MyClassValue.java 

(which of course should be directly attached to the openjdk issue 
tracker issue, and not hyperlinked to the Groovy tracker)


And to reproduce the issue, run:
$ javac MyClassValue.java && javac CVTest.java && mkdir -p t && jar 
cvf t/t.jar MyClassValue*.class && rm MyClassValue*.class && 
JAVA_OPTS=-Xmx4m java CVTest

and wait for the an error to occur, which is:
Exception in thread "main" java.lang.OutOfMemoryError: Compressed 
class space

at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:759)
at 
java.security.SecureClassLoader.defineClass(SecureClassLoader.java:152)

at java.net.URLClassLoader.defineClass(URLClassLoader.java:470)
at java.net.URLClassLoader.access$100(URLClassLoader.java:76)
at java.net.URLClassLoader$1.run(URLClassLoader.java:371)
at java.net.URLClassLoader$1.run(URLClassLoader.java:365)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:364)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
at CVTest.main(CVTest.java:12)

The bug is reproducible on all JDK 8 and 9 builds (I tested up to JDK 
9 build 99).


Based on my understanding of the situation from my research in the 
Groovy bug that discovered the issue ( 
https://issues.apache.org/jira/browse/GROOVY-6704 ) and some work 
another person did with the YourKit profiler ( 
https://www.yourkit.com/forum/viewtopic.php?f=3&t=12658 ), I suspect 
that the fix for https://bugs.openjdk.java.net/browse/JDK-8032606 
"ClassValue.ClassValueMap.type is unused" is relevant; I think the 
problem lies in the java.lang.Class.classValueMap implementation.


Thank you,
~Craig Andrews
___
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev


___
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev