RFR(S): 8173743: Failures during class definition can lead to memory leaks in metaspace
Dmitry Dmitriev
dmitry.dmitriev at oracle.com
Tue Feb 7 10:53:48 UTC 2017
Hello Volker,
Few additional test comments:
1) 168 String self = ManagementFactory.getRuntimeMXBean().getName();
169 String pid = self.substring(0, self.indexOf('@'));
170 System.out.println("Our pid is = " + pid);
I think it's is better to use getProcessId() method from
jdk.test.lib.process.ProcessTools testlibrary
package(test/lib/jdk/test/lib/process/ProcessTools.java) to get pid().
2) 214 Scanner s = new Scanner(result);
Can you please put code for Scanner in the try-with-resources block and
remove 's.close();' on line 226?
3) In case of error getClassStats() prints information about exception
and returns 0. Test will fail later at this check "if (count != ...)
throw new RuntimeException(res);". Probably test should fail(raise an
exception) in getClassStats() method in case of the error to avoid
confusion?
4) Can you please move "(args.length > 1 ? Integer.parseInt(args[1]) :
10)" outside of the for loop in lines 243,277,304,362 to improve
readability? I.e. do something like that:
static final int DEFAULT_DEFINE_ATTEMPTS = 10;
...
public static void main(String[] args) throws Exception {
int defineAttempts = args.length > 1 ? Integer.parseInt(args[1]) :
DEFAULT_DEFINE_ATTEMPTS;
...
for (int i = 0; i < defineAttempts; i++) {
In this case 'defineAttemps' also can be used on lines 332,333 and
'length' can be removed.
5) 338 Thread.sleep(100); // Give the new thread a
chance to start
I don't feel happy with sleep :) For 10 threads we get 1 second sleep in
total. It is possible to use different mechanism to ensure that threads
are start(another CountDownLatch)?
6) I think that you can reduce some code duplication in the main method
by moving following construction to the separate method:
261 count = getClassStats("DefineClass");
262 // At least after System.gc() the failed loading
attempts should leave no instances around!
263 String res = "Should have 2 DefineClass instances and
we have: " + count;
264 System.out.println(res);
265 if (count != 2) throw new RuntimeException(res);
E.g.
void printClassStats(int expectedCount, boolean reportError) {
count = getClassStats("DefineClass");
String res = "Should have " + expectedCount + " DefineClass
instances and we have: " + count;
System.out.println(res);
if (reportError && count != expectedCount) {
throw new RuntimeException(res);
}
}
In this case following piece of code:
254 int count = getClassStats("DefineClass");
255 // We expect to have two instances of DefineClass
here: the initial version in which we are
256 // executing and one another version which was loaded
into our own classloader 'MyClassLoader'.
257 // All the subsequent attempts to reload DefineClass
into our 'MyClassLoader' should have failed.
258 System.out.println("Should have 2 DefineClass
instances and we have: " + count);
259 System.gc();
260 System.out.println("System.gc()");
261 count = getClassStats("DefineClass");
262 // At least after System.gc() the failed loading
attempts should leave no instances around!
263 String res = "Should have 2 DefineClass instances and
we have: " + count;
264 System.out.println(res);
265 if (count != 2) throw new RuntimeException(res);
will be transformed to the following:
// We expect to have two instances of DefineClass here: the initial
version in which we are
// executing and one another version which was loaded into our own
classloader 'MyClassLoader'.
// All the subsequent attempts to reload DefineClass into our
'MyClassLoader' should have failed.
printClassStats(2, false);
System.gc();
System.out.println("System.gc()");
// At least after System.gc() the failed loading attempts should
leave no instances around!
printClassStats(2, true);
Thank you!
Dmitry
On 06.02.2017 21:40, Volker Simonis wrote:
> Hi,
>
> can somebody please review the following change:
>
> http://cr.openjdk.java.net/~simonis/webrevs/2017/8173743.v1/
> https://bugs.openjdk.java.net/browse/JDK-8173743
>
> It fixes some problems during class definitions where instance klasses
> can leak into the metaspace in cases where the class definition fails
> after the class was successfully loaded from the bytecode stream.
>
> There are actually two cases to consider:
>
> 1. If we load the same class several times into the same class loader,
> we will get LinkageErrors after the first successful load. But
> nevertheless we will first construct a new instanceKlass for each new
> load attempt and this instanceKlass will never be deleted.
>
> 2. If we have a parallel capable class loader, set
> -XX:-UnsyncloadClass and/or -XX:+AllowParallelDefineClass and load a
> class from several threads at the same time in parallel, it can happen
> that we create several instance klasses for the same class. At the end
> only one of them will be added to the system dictionary, but all the
> other ones will never be deleted. Notice that if we run this scenario
> without setting either of -XX:-UnsyncloadClass or
> -XX:+AllowParallelDefineClass, this scenario will degrade into the
> case above and we will get LinkageErrors for all but the first
> successful load.
>
> The change comes with a regression test which checks for the two cases
> just describe and also for the failing class redefinition case, which
> currently doesn't produce a memory leak.
>
> I've already committed this to the hs-demo-submit/hotspot/ forest and
> it went through without a problem. So in theory it should have passed
> the internal JPRT tests although I'm not sure if the test set of the
> "demo-submit" forest and the real hotspot repo are exactly the same
> (CC'ed Tim and Brian).
>
> Thank you and best regards,
> Volker
More information about the hotspot-runtime-dev
mailing list