Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Region created but destructor not called? #6787

Open
ddxxdd-code opened this issue Oct 28, 2022 · 4 comments
Open

Region created but destructor not called? #6787

ddxxdd-code opened this issue Oct 28, 2022 · 4 comments

Comments

@ddxxdd-code
Copy link

ddxxdd-code commented Oct 28, 2022

Dear developers,

I’m currently looking at memory usage by OpenJ9 JVM during JIT compilation. I track the usage of memory allocated by segment providers by regions and allocations.

I found some regions in OMR that are created but their destructors never be called. These regions call allocate() and hold memory from segment provider until the end of a compilation. I feel this might imply possible memory leak.

Here is the list of such regions by where they are created:
https://github.com/eclipse/omr/blob/556374a6151cdaac3570316a3bc00c3fd7d9c56f/compiler/infra/OMRCfg.hpp#L101
https://github.com/eclipse/omr/blob/556374a6151cdaac3570316a3bc00c3fd7d9c56f/compiler/infra/OMRCfg.hpp#L108
https://github.com/eclipse/omr/blob/556374a6151cdaac3570316a3bc00c3fd7d9c56f/compiler/optimizer/OMROptimizationManager.cpp#L270
https://github.com/eclipse/omr/blob/556374a6151cdaac3570316a3bc00c3fd7d9c56f/compiler/compile/OMRCompilation.cpp#L321

@AlexeyKhrabrov
Copy link
Contributor

@mpirvu @jdmpapin FYI

@jdmpapin
Copy link
Contributor

jdmpapin commented Oct 28, 2022

The node region is used to allocate nodes, which may live until the end of the compilation, so that one is no problem.

Each CFG has an "internal" and a "structure" memory region. The main CFG lives until the end of the compilation, so that one is fine as well. Inlining creates temporary CFGs for methods under consideration during estimateCodeSize() and also for each actually inlined method during physical inlining. The latter CFGs actually get merged into the main CFG, so their nodes and edges within the "internal" region also live (in general...) until the end of the compilation. The former CFG objects though are allocated within a stack region, and then forgotten, unnecessarily holding on to their regions (and therefore segments). In the compilations I've looked at, there hasn't been much memory tied up in this way though, since most inlined methods have CFGs that are small enough to fit within the region's embedded 4kB buffer.

I've written some changes related to CFG here - that I just haven't yet had a chance to contribute - which have the following effects:

  1. Register inliner's temporary CFGs to be destroyed. This was the motivation for Replace TR::Region::create() with registerDestructor() #6648.
  2. For inlined method CFGs that will be merged into the main CFG, reuse the main CFG's regions instead of creating new ones.

In compilations with a lot of inlining, these changes will dramatically reduce the number of CFG-related regions, which makes my (also yet-to-be-contributed) region tracing/visualization much more efficient and less misleading. (The large number of CFG-related regions seemed to be much more worrisome than it really was.)

The one that really surprises me here is the stack region in performChecks(). How do we manage not to destroy that one?

@ddxxdd-code
Copy link
Author

ddxxdd-code commented Oct 28, 2022

Thanks for detailed explanations for each of the cases!

For that one in performChecks(), it comes from the below hot compilation:

  • (hot) com/ibm/ws/tcpchannel/internal/WorkQueueManager.attemptIO(Lcom/ibm/ws/tcpchannel/internal/TCPBaseRequestContext;Z)Z @ 00007F4520A1BAC8-00007F4520A23A1D OrdinaryMethod G Q_SZ=4805 Q_SZI=3 QW=32612 j9m=000000000080DE38 bcsz=796 OSR time=30369763us mem=[region=107840 system=114688]KB compThreadID=2 CpuLoad=0%(0%avg) JvmCpu=74% queueTime=94031585us

The backtrace of this region's constructor:
In the format of <file>:<line>; <function name>, back from constructor of the region:

/omr/compiler/env/StackMemoryRegion.cpp:30; TR::StackMemoryRegion::StackMemoryRegion(TR_Memory&)
/omr/compiler/optimizer/OMROptimizationManager.cpp:270; OMR::OptimizationManager::performChecks()
/omr/compiler/env/Region.hpp:145; OMR::CFG::invalidateStructure()

The tracked source of this region is from call of OMR::CFG::invalidateStructure(), which results in call to reset(): https://github.com/eclipse/omr/blob/556374a6151cdaac3570316a3bc00c3fd7d9c56f/compiler/env/Region.hpp#L145

I've also checked if this is an issue happens by accident like the compilation is terminated before it completes, but it appeared in all of the four hot compilations I collected data from and they use segment provider to allocate a total of 589824, 262144, 131072, and 131072 bytes in this region. Generally hundreds of KB is caused by this region. Compared to total usage of usually tens or hundred of MB for one compilation, this region's contribution is small.

My current track depth for region's constructor is only three levels back in the stack, I feel I need to extend tracked depth to find the real source in this case.

Will get back to this case after I increase the backtrace depth. Thank you!

@jdmpapin
Copy link
Contributor

Regardless of the calling context, it shouldn't be possible to exit performChecks() without destroying the stack region. Seems like there must be a bug somewhere

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants