JDK-8131702 : PhaseIterGVN(PhaseGVN* gvn) constructor can consume a lot of memory during incremental inlining.
  • Type: Enhancement
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 9
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: generic
  • CPU: generic
  • Submitted: 2015-07-16
  • Updated: 2015-11-20
  • Resolved: 2015-09-14
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Related Reports
Relates :  
Relates :  
That constructor always allocates _stack structure with unique/2 elements array:

PhaseIterGVN::PhaseIterGVN( PhaseGVN *gvn ) : PhaseGVN(gvn),
                                              _stack(C->unique() >> 1),

Before incremental inlining it was allocated only once and it was fine. Now that constructor is used in incremental inlining:

igvn = PhaseIterGVN(gvn);

So each time we execute that call (for each call site inlining) we reallocate new _stack. It is not actual memory leak since it is in thread resource area which will be freed after compilation. But what I observed is with a lot of call sites (Nashorn big methods compilation) its memory consumption  become very noticeable > 400Mbytes of memory (JDK-8129847):

[0x00007fa294497cb6] Arena::grow(unsigned long, AllocFailStrategy::AllocFailEnum)+0x46
[0x00007fa294b1681d] PhaseIterGVN::PhaseIterGVN(PhaseGVN*)+0x39d
[0x00007fa29465143d] Compile::inline_incrementally_one(PhaseIterGVN&)+0x13d
[0x00007fa294654beb] Compile::inline_incrementally(PhaseIterGVN&)+0x34b
                             (malloc=426040KB #325)

It is used only in remove_globally_dead_node() to avoid recursion. I cached it in PhaseIterGVN class to avoid allocation each time in that method which is very frequent. And it works fine before incremental inlining implementation.

There are few solutions which I can think of.

One is create new  PhaseIterGVN constructor to use in incremental inlining code and copy _stack from original igvn.

An other is to move _stack to Compile class. The problem with that is we don't know what size should be - may go with 32 as I did. It is not used until first IGVN optimization. So we may as well create new stack and copy to Compile::_stack when we know number of live nodes.

Or simple do next and allow small leak without modifying code a lot:

 PhaseIterGVN::PhaseIterGVN( PhaseGVN *gvn ) : PhaseGVN(gvn),
-                                              _stack(C->unique() >> 1),
+                                              _stack(C->comp_arena(), 32),

An other problem is it uses thread local resource area by default. And I hit problem when I set small size because I think there is somewhere ResourceMark which unwind thread resource area. But _stack was grown and used that area. That is why I used compiler arena  above which is not collected by default ResourceMark.

Thank you, Vladimir!

Okay. I agree with you closing this bug and back porting JDK-8011858.

Hi Vladimir, yes, I tried the same initial size. The stack is created before PhaseIterGVN is created, // Iterative Global Value Numbering, including ideal transforms // Initialize IterGVN with types and values from parse-time GVN ->Node_Stack stack(Thread::current()->resource_area(), 32); PhaseIterGVN igvn(initial_gvn(), stack); and then is reused in all constructors of PhaseIterGVN executed afterwards. Thank you and best regards, Zoltan

So you are saying the _stack(C->comp_arena(), 32) works better? When you reused the same stack what was initial size of stack? Did you tried the same 32 size?

Hi Vladimir, Vlad's patch for JDK-8011858 includes also the fix you've suggested in the description of this problem. http://hg.openjdk.java.net/jdk9/hs-comp/hotspot/rev/af60f1cb36f2#l10.17 I also tried a second solution that reuses the same stack instance for all PhaseIterGVN's constructed for incremental inlining (you've also suggested that in the descriptor). The solution results in higher memory usage than Vlad's patch. I measured the RSS (in MB) of the JVM during the compilation of a method into which C2 inlines a lot of other methods: jdk.nashorn.internal.scripts.Script$Recompilation$181$345772AA$r::L:100$loadLib$L:8141$prog1: Compiler phase | Default (8u-dev) | Vlad's patch | Second solution ============================================================ PHASE_AFTER_PARSING | 196 | 194 | 205 PHASE_ITER_GVN1 | 196 | 194 | 205 PHASE_INCREMENTAL_INLINE | 478 | 273 | 292 PHASE_INCREMENTAL_BOXING_INLINE | 478 | 273 | 292 PHASE_ITER_GVN_AFTER_EA | 488 | 286 | 310 PHASE_OPTIMIZE_FINISHED | 510 | 307 | 347 After compilation | 545 | 505 | 561 Details about the experimental setup are described in JDK-8129847. It seems that Vlad's patch reduces the memory consumption of incremental inlining sufficiently well (by around 3.5X), therefore I'll do the following: - file a backport of the current issue into 8u (I'm not sure what the appropriate version relative to should be, I've set it temporarily to 8u71); - close the current issue as "Resolved -- Not an Issue". Please let me know if you think it would have been better to proceed differently. Thank you and best regards, Zoltan

Issue fixed by JDK-8011858.

Hi Vladimir, I'd like to look into this problem as it is related to JDK-8129847. Do you mind if I assign it to myself? If you would rather be looking at it, just please assign it back to yourself. Thank you and best regards, Zoltan