22

UPD 21.11.2017: the bug is fixed in JDK, see comment from Vicente Romero

Summary:

If for statement is used for any Iterable implementation the collection will remain in the heap memory till the end of current scope (method, statement body) and won't be garbage collected even if you don't have any other references to the collection and the application needs to allocate a new memory.

http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8175883

https://bugs.openjdk.java.net/browse/JDK-8175883

The example:

If i have the next code, which allocates a list of large strings with random content:

import java.util.ArrayList;
public class IteratorAndGc {
    
    // number of strings and the size of every string
    static final int N = 7500;

    public static void main(String[] args) {
        System.gc();

        gcInMethod();

        System.gc();
        showMemoryUsage("GC after the method body");

        ArrayList<String> strings2 = generateLargeStringsArray(N);
        showMemoryUsage("Third allocation outside the method is always successful");
    }

    // main testable method
    public static void gcInMethod() {

        showMemoryUsage("Before first memory allocating");
        ArrayList<String> strings = generateLargeStringsArray(N);
        showMemoryUsage("After first memory allocation");


        // this is only one difference - after the iterator created, memory won't be collected till end of this function
        for (String string : strings);
        showMemoryUsage("After iteration");

        strings = null; // discard the reference to the array

        // one says this doesn't guarantee garbage collection,
        // Oracle says "the Java Virtual Machine has made a best effort to reclaim space from all discarded objects".
        // but no matter - the program behavior remains the same with or without this line. You may skip it and test.
        System.gc();

        showMemoryUsage("After force GC in the method body");

        try {
            System.out.println("Try to allocate memory in the method body again:");
            ArrayList<String> strings2 = generateLargeStringsArray(N);
            showMemoryUsage("After secondary memory allocation");
        } catch (OutOfMemoryError e) {
            showMemoryUsage("!!!! Out of memory error !!!!");
            System.out.println();
        }
    }
    
    // function to allocate and return a reference to a lot of memory
    private static ArrayList<String> generateLargeStringsArray(int N) {
        ArrayList<String> strings = new ArrayList<>(N);
        for (int i = 0; i < N; i++) {
            StringBuilder sb = new StringBuilder(N);
            for (int j = 0; j < N; j++) {
                sb.append((char)Math.round(Math.random() * 0xFFFF));
            }
            strings.add(sb.toString());
        }

        return strings;
    }

    // helper method to display current memory status
    public static void showMemoryUsage(String action) {
        long free = Runtime.getRuntime().freeMemory();
        long total = Runtime.getRuntime().totalMemory();
        long max = Runtime.getRuntime().maxMemory();
        long used = total - free;
        System.out.printf("\t%40s: %10dk of max %10dk%n", action, used / 1024, max / 1024);
    }
}

compile and run it with limited memory, like this (180mb):

javac IteratorAndGc.java   &&   java -Xms180m -Xmx180m IteratorAndGc

and at runtime i have:

Before first memory allocating: 1251k of max 176640k

After first memory allocation: 131426k of max 176640k

After iteration: 131426k of max 176640k

After force GC in the method body: 110682k of max 176640k (almost nothing collected)

Try to allocate memory in the method body again:

     !!!! Out of memory error !!!!:     168948k of max     176640k

GC after the method body: 459k of max 176640k (the garbage is collected!)

Third allocation outside the method is always successful: 117740k of max 163840k

So, inside gcInMethod() i tried to allocate the list, iterate over it, discard the reference to the list, (optional)force garbage collection and allocate similar list again. But i can't allocate second array because of lack of memory.

In the same time, outside the function body i can successfully force garbage collection (optional) and allocate the same array size again!

To avoid this OutOfMemoryError inside the function body it's enough to remove/comment only this one line:

for (String string : strings); <-- this is the evil!!!

and then output looks like this:

Before first memory allocating: 1251k of max 176640k

After first memory allocation: 131409k of max 176640k

After iteration: 131409k of max 176640k

After force GC in the method body: 497k of max 176640k (the garbage is collected!)

Try to allocate memory in the method body again:

After secondary memory allocation: 115541k of max 163840k

GC after the method body: 493k of max 163840k (the garbage is collected!)

Third allocation outside the method is always successful: 121300k of max 163840k

So, without for iterating the garbage successfully collected after discarding the reference to the strings, and allocated second time (inside the function body) and allocated third time (outside the method).

My supposition:

for syntax construction is compiled to

Iterator iter = strings.iterator();
while(iter.hasNext()){
    iter.next()
}

(and i checked this decompiling javap -c IteratorAndGc.class)

And looks like this iter reference stays in the scope till the end. You don't have access to the reference to nullify it, and GC can't perform the collection.

Maybe this is normal behavior (maybe even specified in javac, but i haven't found), but IMHO if compiler creates some instances it should care about discarding them from the scope after using.

That's how i expect to have the implementation of for statement:

Iterator iter = strings.iterator();
while(iter.hasNext()){
    iter.next()
}
iter = null; // <--- flush the water!

Used java compiler and runtime versions:

javac 1.8.0_111

java version "1.8.0_111"
Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)

Note:

  • the question is not about programming style, best practices, conventions and so on, the question is about an efficiency of Java platform.

  • the question is not about System.gc() behavior (you may remove all gc calls from the example) - during the second strings allocation the JVM must release the dicarded memory.

Reference to the test java class, Online compiler to test (but this resource has only 50 Mb of heap, so use N = 5000)

Community
  • 1
  • 1
radistao
  • 14,889
  • 11
  • 66
  • 92
  • 2
    You incorrectly understand how does GC work. There is no guarantee that GC will collect anything after one call. – Andremoniy Feb 22 '17 at 22:33
  • 1
    Damn, read the description, pls! the question not about GC call!!! "the question is not about System.gc() behavior (you may remove all gc calls from the example) - during the second strings allocation the JVM must release the dicarded memory." The question is about "for" implementation. – radistao Feb 22 '17 at 22:49
  • @Andremoniy pls, read the description and remove the duplication flag - the question is about "for" statement implementation, not about System.gc() call – radistao Feb 22 '17 at 22:54
  • You do not seem to consider that, to use the `Iterator` it also has load the `Iterator` class, the implementation _and_ all of it's used dependencies. Those take up memory too. You could check the difference with an `-XX:+TraceClassLoading` VM argument. – Jorn Vernee Feb 22 '17 at 22:58
  • 2
    you think Iterator and related class loading should take 150Mb of memory? guess no. Also, when i leave the function body those Iterator Classes are not removed, but the memory is released! Also, classes are loaded to own memory, not to the heap. – radistao Feb 22 '17 at 23:01
  • 1
    anyway: tested with `-XX:+TraceClassLoading`: all classes are loaded before the first memory allocating. – radistao Feb 22 '17 at 23:05
  • 3
    Compared to what? *Any* way you traverse the collection, you have to retain a reference to it. The 'problem' is with the fact that you're traversing the collection, not with the 'implementation' of anything. NB 'during the second strings allocation the JVM must release the dicarded memory' is false. Please lay off the bold face. It hurts my eyes. – user207421 Feb 22 '17 at 23:11
  • Do you get any difference if you change this "enhanced for" to what it should be equivalent to (see http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.14.2)? – avysk Feb 22 '17 at 23:18
  • Why i should retain a reference to a collection if i traverse it? Why JVM may skip garbage collection if the application requires a memory? – radistao Feb 22 '17 at 23:20
  • (a) How exactly could you traverse it *without* a reference to it? (b) The JVM only needs to run GC when it runs *out* of memory. – user207421 Feb 22 '17 at 23:21
  • 1
    @avysk if i use `for (Iterator itr = strings.iterator(); itr.hasNext(); itr.next());` it also causes OutOfMemory, if i use `while(iter.hasNext()){iter.next()}; iter = null;` - it works fine, because i discard the instance to the iterator explicitly. – radistao Feb 22 '17 at 23:29
  • 2
    @EJP and it runs OUT OF MEMORY! And i also described in the question how the compiler compiled the "for" statement - it compiled to iterator-while structure. – radistao Feb 22 '17 at 23:31
  • @radistao yep, it's beautiful. I've tried your code -- just changing it to boolean go = true; if (go) { for (String string:strings); } solves the problem :D – avysk Feb 22 '17 at 23:33
  • @avysk Either you are suggesting that the compiler doesn't comply with the JLS or your question is entirely pointless. Which is it? – user207421 Feb 22 '17 at 23:34

6 Answers6

15

Thanks for the bug report. We have fixed this bug, see JDK-8175883. As commented here in the case of the enhanced for, javac was generating synthetic variables so for a code like:

void foo(String[] data) {
    for (String s : data);
}

javac was approximately generating:

for (String[] arr$ = data, len$ = arr$.length, i$ = 0; i$ < len$; ++i$) {
    String s = arr$[i$];
}

as mentioned above this translation approach implies that the synthetic variable arr$ holds a reference to the array data that impedes the GC to collect the array once it is not referred anymore inside the method. This bug has been fixed by generating this code:

String[] arr$ = data;
String s;
for (int len$ = arr$.length, i$ = 0; i$ < len$; ++i$) {
    s = arr$[i$];
}
arr$ = null;
s = null;

The idea is to set to null any synthetic variable of a reference type created by javac to translate the loop. If we were talking about an array of a primitive type, then the last assignment to null is not generated by the compiler. The bug has been fixed in repo JDK repo

Vicente Romero
  • 1,460
  • 13
  • 16
  • sure @radistao, np – Vicente Romero Nov 21 '17 at 22:00
  • That's not a fix, that's a round of whack-a-mole. If instead any reference that goes out of scope would be nullified, this hack would not be needed, nor would developers have to worry about other similar situations. Superfluous nullifications (where the location is subsequently reassigned) can easily be micro-optimised away (right?), so there should not be any performance impact. – RFST May 20 '18 at 02:55
  • Unfortunately, neither solution (nullifying all references when they go out of scope, or this hack), would work when an exception is thrown. Or would it, and why? BTW, these issues are face-palm territory to C++ programmers (RAII rules!)... – RFST May 20 '18 at 03:46
  • 1
    @RFST the principle of managed code with garbage collection is that memory is not a resource in the RAII sense. I understand your impression, but that is caused by the overemphasis of the “problem”. Actually, local variables do not prevent the collection of their referent. These dangling references are only a problem in the rare corner case of processing large data without the JIT kicking in (as only with interpreted execution the JVM doesn’t recognize that these references are unused). Even then, this loop case could be fixed on the JVM side, by utilizing the stackmap tables. – Holger Feb 19 '20 at 09:06
5

So this is actually an interesting question that could have benefited from a slightly different wording. More specifically, focusing on the generated bytecode instead would have cleared a lot of the confusion. So let's do that.

Given this code:

List<Integer> foo = new ArrayList<>();
for (Integer i : foo) {
  // nothing
}

This is the generated bytecode:

   0: new           #2                  // class java/util/ArrayList
   3: dup           
   4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
   7: astore_1      
   8: aload_1       
   9: invokeinterface #4,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
  14: astore_2      
  15: aload_2       
  16: invokeinterface #5,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
  21: ifeq          37
  24: aload_2       
  25: invokeinterface #6,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
  30: checkcast     #7                  // class java/lang/Integer
  33: astore_3      
  34: goto          15

So, play by play:

  • Store the new list in local variable 1 ("foo")
  • Store the iterator in local variable 2
  • For each element, store the element in local variable 3

Note that after the loop, there's no cleanup of anything that was used in the loop. That isn't restricted to the iterator: the last element is still stored in local variable 3 after the loop ends, even though there's no reference to it in the code.

So before you go "that's wrong, wrong, wrong", let's see what happens when I add this code after that code above:

byte[] bar = new byte[0];

You get this bytecode after the loop:

  37: iconst_0      
  38: newarray       byte
  40: astore_2      

Oh, look at that. The newly declared local variable is being stored in the same "local variable" as the iterator. So now the reference to the iterator is gone.

Note that this is different from the Java code you assume is the equivalent. The actual Java equivalent, which generates the exact same bytecode, is this:

List<Integer> foo = new ArrayList<>();
for (Iterator<Integer> i = foo.iterator(); i.hasNext(); ) {
  Integer val = i.next();
}

And still there's no cleanup. Why's that?

Well, here we are in guessing territory, unless it's actually specified in the JVM spec (haven't checked). Anyway, to do cleanup, the compiler would have to generate extra bytecode (2 instructions, aconst_null and astore_<n>) for each variable that's going out of scope. This would mean the code runs slower; and to avoid that, possibly complicated optimizations would have to be added to the JIT.

So, why does your code fail?

You end up in a similar situation as the above. The iterator is allocated and stored in local variable 1. Then your code tries to allocate the new string array and, because local variable 1 is not in use anymore, it would be stored in the same local variable (check the bytecode). But the allocation happens before the assignment, so there's a reference to the iterator still, so there's no memory.

If you add this line before the try block, things work, even if you remove the System.gc() call:

int i = 0;

So, it seems the JVM developers made a choice (generate smaller / more efficient bytecode instead of explicitly nulling variables that go out of scope), and you happen to have written code that doesn't behave well under the assumptions they made about how people write code. Given that I've never seen this problem in actual applications, seems like a minor thing to me.

vanza
  • 9,715
  • 2
  • 31
  • 34
  • this looks for me the most applicable answer: the "automatically generated identifier" for iterator reference keeps the memory till next usage of the identifier. – radistao Feb 23 '17 at 22:54
  • but even though: 1) if it is a corner case - it must be accounted (does a good developer skips corner cases?); 2) the platform (compiler+VM) should not work in undefined manner, when variable may be or may be not reused thus memory released or not; 3) making choice for "more efficient" sacrificing stability and determinism seems not a good choice; 4) this "corner case" i found in my working application, when after a big XML parsing and iterating i came to out of memory state. – radistao Feb 24 '17 at 22:51
4

The only relevant part of the enhanced for statement, here, is the extra local reference to the object.

Your example can be reduced to

public class Example {
    private static final int length = (int) (Runtime.getRuntime().maxMemory() * 0.8);

    public static void main(String[] args) {
        byte[] data = new byte[length];
        Object ref = data; // this is the effect of your "foreach loop"
        data = null;
        // ref = null; // uncommenting this also makes this complete successfully
        byte[] data2 = new byte[length];
    }
}

This program will also fail with an OutOfMemoryError. If you remove the ref declaration (and its initialization), it will complete successfully.

The first thing you need to understand is that scope has nothing to do with garbage collection. Scope is a compile time concept that defines where identifiers and names in a program's source code can be used to refer to program entities.

Garbage collection is driven by reachability. If the JVM can determine that an object cannot be accessed by any potential continuing computation from any live thread, then it will consider it eligible for garbage collection. Also, the System.gc() is useless because the JVM will perform a major collection if it cannot find space to allocate a new object.

So the question becomes: why can't the JVM determine that the byte[] object is no longer accessed if we store it in a second local variable?

I don't have an answer for that. Different garbage collection algorithms (and JVMs) may behave differently in that regard. It seems that this JVM doesn't mark the object as unreachable when a second entry in the local variable table has a reference to that object.


Here's a different scenario where the JVM didn't behave exactly as you migth have expected in regards to garbage collection:

Community
  • 1
  • 1
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • in the example you keep the reference to the byte array in the _ref_ object, isn't it? That's why it can't be collected. If i remove the _ref_ declaration or set to **null** - the memory may be released ("makes this complete successfully" as you say. But I think the compiler or JVM should not create any "dead" references, which lock the memory, but not used any more. – radistao Feb 22 '17 at 23:54
  • thanks for reference to "OutOfMemoryError when seemingly unrelated code block commented out" - i'll try also to find in the bytecode references for the `for` loop (but not much experienced in reading decompiled codes though) – radistao Feb 22 '17 at 23:55
  • 1
    @radistao You don't have to read byte code. The JLS defines how an [enhanced for statement](http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.14.2) is compiled. There's an extra reference to the `Iterator` (which references your object), just like you suggested. – Sotirios Delimanolis Feb 22 '17 at 23:56
  • `#i is an automatically generated identifier that is distinct from any other identifiers (automatically generated or otherwise) that are in scope (§6.3) at the point where the enhanced for statement occurs.` That's what i was looking for! thanks! could you put it as a separate answer - i'd mark it – radistao Feb 23 '17 at 00:02
  • "Now the second question remains: is it reasonable (for me sounds "yes") that the "automatically generated identifier" should be discarded after the usage? – radistao Feb 23 '17 at 00:04
  • @radistao The enhanced for statement is addressed [here](http://stackoverflow.com/questions/85190/how-does-the-java-for-each-loop-work). – Sotirios Delimanolis Feb 23 '17 at 00:06
  • @radistao As for the identifier, there's no such thing as _discarding it_. Java compiles local variable usage into a local variable table (in the .class file) which defines which variable is active at given points in the byte code. – Sotirios Delimanolis Feb 23 '17 at 00:07
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/136404/discussion-between-radistao-and-sotirios-delimanolis). – radistao Feb 23 '17 at 00:12
  • 2
    @Sotirios Delimanolis: no, the local variable table is an optional debugging attribute that is irrelevant to the execution. The existence of local variables is purely determined by reads and writes into locations of the stack frame. That’s why the iterator reference is dangling after the `for` loop: there is no other variable causing its location to be overwritten with a new value. In practice, this will be irrelevant, as once the optimizer kicks in, it will do its own usage analysis, sometimes causing objects to be freed much earlier than you would expect. But here, it likely runs interpreted – Holger Feb 23 '17 at 15:50
4

As already stated in the other answers, there the concept of variable scopes is not known at runtime. In the compiled class files, local variables are only places within a stack frame (addressed by an index), to which writes and reads are performed. If multiple variable have disjunct scopes, they may use the same index, but there is no formal declaration of them. Only the write of a new value discards the old one.

So, there are three ways, how a reference held in a local variable storage can be considered unused:

  1. The storage location is overwritten by a new value
  2. The method exits
  3. No subsequent code reads the value

It should be obvious that the third point is the hardest to check, hence, it does not always apply, but when the optimizer starts its work, it may lead to surprises in the other direction, as explained in “Can java finalize an object when it is still in scope?” and “finalize() called on strongly reachable object in Java 8”.

In your case, the application runs very shortly and likely non-optimized, which can lead to references being not recognized as being unused due to point 3, when point 1 and 2 do not apply.

You can easily verify that this is the case. When you change the line

ArrayList<String> strings2 = generateLargeStringsArray(N);

to

ArrayList<String> strings2 = null;
strings2 = generateLargeStringsArray(N);

the OutOfMemoryError goes away. The reason is that the storage location holding the Iterator used in the preceding for loop has not been overwritten at this point. The new local variable strings2 will reuse the storage, but this only manifests when a new value is actually written to it. So the initialization with null before calling generateLargeStringsArray(N) will overwrite the Iterator reference and allows the old list to be collected.

Alternatively, you can run the program in the original form using the option -Xcomp. This forces compilation of all methods. On my machine, it had a noticeable startup slowdown, but due to the variable usage analysis, the OutOfMemoryError also went away.

Having an application that allocates that much memory (compared to the max heap size) during the initialization, i.e. when most methods run interpreted, is an unusual corner case. Usually, most hot methods are sufficiently compiled before the memory consumption is that high. If you encounter this corner case repeatedly in a real life application, well, then -Xcomp might work for you.

Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765
1

Finally, Oracle/Open JKD bug is accepted, approved and fixed:

http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8175883

https://bugs.openjdk.java.net/browse/JDK-8175883

Quoting the comments from the threads:

This is an issue reproducible both on 8 and 9

There is some issue the program keeps it's own implicit auto-generated reference to the memory block till next implicit usage and its memory is being locked that causing OOM

(this proves @vanza's expectation, see this example from the JDK developer)

According the spec this should not happen

(this is an answer to my question: if compiler creates some instances it should care about discarding them from the scope after using)

UPD 21.11.2017: the bug is fixed in JDK, see comment from Vicente Romero

radistao
  • 14,889
  • 11
  • 66
  • 92
0

Just to summarize the answers:

As @sotirios-delimanolis mentioned in his comment about The enhanced for statement - my assumption is explicitly defined: the for sugar statement is compiled to Iterator with hasNext()-next() calls:

#i is an automatically generated identifier that is distinct from any other identifiers (automatically generated or otherwise) that are in scope (§6.3) at the point where the enhanced for statement occurs.

As then @vanza showed in his answer: this automatically generated identifier might be or might be not overridden later. If it is overridden - the memory may be released, if not - the memory is not released any more.

Still (for me) is open question: if Java compiler or JVM creates some implicit references, shouldn't it care then later about discarding those references? Is there any guarantee that the same auto-generated iterator reference will be reused in the next calls before the next memory allocation? Shouldn't it be a rule: those who allocate memory then care about releasing it? I'd say - it must care about this. Otherwise the behavior is undefined (it may fall to OutOfMemoryError, or may not - who knows...)

Yes, my example is a corner case (nothing initialized between for iterator and the next memory allocation), but this doesn't mean it is impossible case. And this doesn't mean this case is hard to achieve - its quite probable to work in a limited memory environment with some large data and re-allocate the memory immediately it has been used. I found this case in my working application where I parse a large XML, which "eats" more than a half of memory.

(and the question is not only about iterator and for loops, guess it is common issue: the compiler or JVM sometimes doesn't cleanup own implicit references).

Community
  • 1
  • 1
radistao
  • 14,889
  • 11
  • 66
  • 92
  • 1
    Well, as stated in my answer, if you really encounter this unusual scenario and have that memory pressure, there is an option that allows you to trade some CPU cycles for fixing this. Your case is also unusual in that it creates a large list, iterates over it once and drops it afterwards. Usually, lists have a much longer lifetime than an iterator and it is not the iterator’s storage that hurts… – Holger Feb 24 '17 at 18:00
  • If even it is a corner case - it should be processed correct. When you create an application do you skip corner cases processing and testing? Such things as a java application should work in a predictable way even in rare cases. And should not relay on "a reference may be or may be not reused by other calls". There is also **no any guarantee that that auto-generated reference will be reused** and the memory released! I found this case in my practice, that's why i post the problem here. – radistao Feb 24 '17 at 22:32
  • 1
    I’m afraid, if you assume “a java application should work in a predictable way” and we’re talking only about memory consumption and performance, Java might be the wrong thing for you. You can’t assume instantaneous release of memory, you can’t predict the maximum recursion depth and there is no guaranty about the performance. There is no guaranty made in the specification that would be violated here. Besides that, as already said, your code works as expected, if you run it with the `-Xcomp` option. – Holger Feb 27 '17 at 09:51
  • -Xcomp is "Non-Standard Options" (http://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html), thus it may have different interpretation for different JVM implementations (here it is deprecated: https://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.zos.80.doc/diag/appendixes/cmdline/xcomp.html) – radistao Feb 27 '17 at 13:54
  • And i can't understand why my assumption “a java application should work in a predictable way” sounds wrong? We all want to use predictable applications - that's the main point of stability. There is nothing about performance, recursion, stack depth and "instantaneous release" - Java promises to release unused memory. That memory is unused and not released. If JVM needs a time to release - take it, but don't complain "the developer has unpredictable coding style and has not reused my automatic linked references" – radistao Feb 27 '17 at 13:58
  • 1
    The promise to release unused memory is only made on a best effort basis. Now, just think about it: if Java never managed to release that memory, it would be a predictable behavior. But some JVMs manage to release that memory under certain conditions, so is this making the situation better or worse? Besides that, there is no defined memory consumption anyway. A different JVM could always manage to release that memory, but consume twice the memory per object in general, thus still failing with that code. Does that make the situation better? How much memory you need *is* implementation-specific. – Holger Feb 27 '17 at 14:22
  • But if you think, this is a bug, you may file a bug report, rather than discuss it on Stackoverflow… – Holger Feb 27 '17 at 14:24
  • I did it, waiting for confirmation from JDK developers :) – radistao Feb 27 '17 at 14:53
  • @Holger finally the bug is accepted, see my comment: http://stackoverflow.com/a/42556668/907576 . I still can't write them directly you advise about `-Xcomp` option because the issue tracker is private and doesn't have public registration. Will try to e-mail them. – radistao Mar 02 '17 at 14:25