Skip to content

8299915: Remove ArrayAllocatorMallocLimit and associated code #11931

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

Closed
wants to merge 12 commits into from

Conversation

jcking
Copy link
Contributor

@jcking jcking commented Jan 10, 2023

Remove abstraction that is a holdover from Solaris. Direct usages of MmapArrayAllocator have been switched to normal malloc. The justification is that none of the code paths are called from signal handlers, so using mmap directly does not make sense and is potentially slower than going through malloc which can potentially re-use memory without making any system calls. The remaining usages of ArrayAllocator and MallocArrayAllocator are equivalent.


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Issue

  • JDK-8299915: Remove ArrayAllocatorMallocLimit and associated code

Contributors

  • Stefan Karlsson <stefank@openjdk.org>

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/11931/head:pull/11931
$ git checkout pull/11931

Update a local copy of the PR:
$ git checkout pull/11931
$ git pull https://git.openjdk.org/jdk.git pull/11931/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 11931

View PR using the GUI difftool:
$ git pr show -t 11931

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/11931.diff

Webrev

Link to Webrev Comment

Signed-off-by: Justin King <jcking@google.com>
Signed-off-by: Justin King <jcking@google.com>
@bridgekeeper
Copy link

bridgekeeper bot commented Jan 10, 2023

👋 Welcome back jcking! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@jcking jcking marked this pull request as ready for review January 10, 2023 20:57
@openjdk openjdk bot changed the title JDK-8299915: Remove ArrayAllocatorMallocLimit 8299915: Remove ArrayAllocatorMallocLimit and associated code Jan 10, 2023
@openjdk openjdk bot added the rfr Pull request is ready for review label Jan 10, 2023
@openjdk
Copy link

openjdk bot commented Jan 10, 2023

@jcking The following labels will be automatically applied to this pull request:

  • hotspot
  • serviceability

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing lists. If you would like to change these labels, use the /label pull request command.

@openjdk openjdk bot added serviceability serviceability-dev@openjdk.org hotspot hotspot-dev@openjdk.org labels Jan 10, 2023
@mlbridge
Copy link

mlbridge bot commented Jan 10, 2023

Signed-off-by: Justin King <jcking@google.com>
Signed-off-by: Justin King <jcking@google.com>
Signed-off-by: Justin King <jcking@google.com>
@stefank
Copy link
Member

stefank commented Jan 11, 2023

I'm happy to see this flag getting removed. I'm less happy about seeing usages of the array allocators being replaced by macros. I'd rather see an effort towards getting rid of these macros. Could we limit this patch to only remove the ArrayAllocatorMallocLimit flag and ArrayAllocator class, and defer the discussion around what API to use for array allocations?

@jcking
Copy link
Contributor Author

jcking commented Jan 11, 2023

I'm happy to see this flag getting removed. I'm less happy about seeing usages of the array allocators being replaced by macros. I'd rather see an effort towards getting rid of these macros. Could we limit this patch to only remove the ArrayAllocatorMallocLimit flag and ArrayAllocator class, and defer the discussion around what API to use for array allocations?

ArrayAllocator with ArrayAllocatorMallocLimit removed is effectively MallocArrayAllocator. Are you suggesting leaving MallocArrayAllocator and MmapArrayAllocator thus update references to ArrayAllocator to be MallocArrayAllocator?

As far as APIs, the majority of the codebase uses the macros. IMO consistency would be better and having two ways of doing things doesn't help. But if you feel strongly about it, we can punt and just surgically remove the bare minimum, assuming you clarify your expectation (see above).

@tstuefe
Copy link
Member

tstuefe commented Jan 11, 2023

Curious, I always thought we do ArrayAllocator - using mmap for larger allocations - to prevent memory retention for libc variants whose allocators are "grabby", i.e. which don't promptly return memory to the OS on free(). E.g. because they only use sbrk (Solaris, AIX), or are just cautious about returning memory (glibc).

Glibc's retention problem is only relevant for fine-grained allocations, so for glibc this is probably fine. This leaves at least AIX as a potential problem. @backwaterred, does the AIX libc malloc() still exclusively use the data segment ? Does free'd memory still stick to the process?

(While writing this, I remember that we at SAP even rewrote Arena allocation to use mmap for AIX, because large compile arenas caused lasting RSS increase, so it has definitely been a problem in the past)

@stefank
Copy link
Member

stefank commented Jan 11, 2023

ArrayAllocator with ArrayAllocatorMallocLimit removed is effectively MallocArrayAllocator. Are you suggesting leaving MallocArrayAllocator and MmapArrayAllocator thus update references to ArrayAllocator to be MallocArrayAllocator?

Yes, that was what I wanted.

As far as APIs, the majority of the codebase uses the macros. IMO consistency would be better and having two ways of doing things doesn't help. But if you feel strongly about it, we can punt and just surgically remove the bare minimum, assuming you clarify your expectation (see above).

I agree about the argument about consistency, so I retract my objection. We can deal with these macros as a separate RFE (if we ever get to it).

I would like to retain the usage of mmapped memory for ZGC to minimize the risk of any surprises for us. ZGC code also tend to initialize as much as possible in the initialization list, so the extra memset that comes after initialization lists sticks out a bit. Could you create a private ZGranuleMap::allocate_array function that uses os::reserve_memory/commmit_memory and change the code to be:

inline ZGranuleMap<T>::ZGranuleMap(size_t max_offset) :
    _size(max_offset >> ZGranuleSizeShift),
    _map(allocate_array(_size)) {

Or if you like, I can provide a patch on top of your branch to do that.

Signed-off-by: Justin King <jcking@google.com>
@jcking
Copy link
Contributor Author

jcking commented Jan 11, 2023

ArrayAllocator with ArrayAllocatorMallocLimit removed is effectively MallocArrayAllocator. Are you suggesting leaving MallocArrayAllocator and MmapArrayAllocator thus update references to ArrayAllocator to be MallocArrayAllocator?

Yes, that was what I wanted.

As far as APIs, the majority of the codebase uses the macros. IMO consistency would be better and having two ways of doing things doesn't help. But if you feel strongly about it, we can punt and just surgically remove the bare minimum, assuming you clarify your expectation (see above).

I agree about the argument about consistency, so I retract my objection. We can deal with these macros as a separate RFE (if we ever get to it).

I would like to retain the usage of mmapped memory for ZGC to minimize the risk of any surprises for us. ZGC code also tend to initialize as much as possible in the initialization list, so the extra memset that comes after initialization lists sticks out a bit. Could you create a private ZGranuleMap::allocate_array function that uses os::reserve_memory/commmit_memory and change the code to be:

inline ZGranuleMap<T>::ZGranuleMap(size_t max_offset) :
    _size(max_offset >> ZGranuleSizeShift),
    _map(allocate_array(_size)) {

Or if you like, I can provide a patch on top of your branch to do that.

The extra memset is due to malloc not handing out zero initialized memory, it looks like zGranuleMap is sensitive to that.

Done. Restored mmap as suggested for zGranuleMap.

@jcking
Copy link
Contributor Author

jcking commented Jan 11, 2023

Curious, I always thought we do ArrayAllocator - using mmap for larger allocations - to prevent memory retention for libc variants whose allocators are "grabby", i.e. which don't promptly return memory to the OS on free(). E.g. because they only use sbrk (Solaris, AIX), or are just cautious about returning memory (glibc).

Glibc's retention problem is only relevant for fine-grained allocations, so for glibc this is probably fine. This leaves at least AIX as a potential problem. @backwaterred, does the AIX libc malloc() still exclusively use the data segment ? Does free'd memory still stick to the process?

(While writing this, I remember that we at SAP even rewrote Arena allocation to use mmap for AIX, because large compile arenas caused lasting RSS increase, so it has definitely been a problem in the past)

Correct, glibc always mmap's allocations above a certain limit and always unmaps them on free. I believe most implementations do that, either immediately unmapping or using madvise. AIX still uses sbrk, based on their documentation that I could find. Though AIX does have something called malloc disclaim.

Signed-off-by: Justin King <jcking@google.com>
@coleenp
Copy link
Contributor

coleenp commented Jan 11, 2023

To follow up on @tstuefe comment - and the one that I tried to say in the bug was that we added this MmapArrayAllocate feature for some G1 marking bits that used so much memory that hit the Solaris _sbrk issue. Maybe @stefank and @tschatzl remember this issue. Maybe it's ok for AIX, then removing this code is a good change. Maybe the G1 usages need a mmap implementation though.

Signed-off-by: Justin King <jcking@google.com>
@jcking
Copy link
Contributor Author

jcking commented Jan 11, 2023

To follow up on @tstuefe comment - and the one that I tried to say in the bug was that we added this MmapArrayAllocate feature for some G1 marking bits that used so much memory that hit the Solaris _sbrk issue. Maybe @stefank and @tschatzl remember this issue. Maybe it's ok for AIX, then removing this code is a good change. Maybe the G1 usages need a mmap implementation though.

The padding.inline.hpp usage seems to have one caller which is called once. The other mmap usage in G1 we can convert to mmap using a similar approach to zGranuleMap if that is preferred. That would then be equivalent behavior, it looks like the G1 code uses the page allocation granularity anyway so maybe keeping it mmap is the better way to go here anyway?

Copy link
Member

@stefank stefank left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the ZGC update. I have a request to slightly modify the style a bit. Would you mind taking this patch:
https://github.com/stefank/jdk/tree/pr_11931

There's a FIXME about that allocation.inline.hpp file is now empty. Will you remove it in this patch? I'd prefer if you did. Alternatively, remove the FIXME from this patch and then immediately clean this up as a separate patch.

@tstuefe
Copy link
Member

tstuefe commented Jan 12, 2023

To follow up on @tstuefe comment - and the one that I tried to say in the bug was that we added this MmapArrayAllocate feature for some G1 marking bits that used so much memory that hit the Solaris _sbrk issue. Maybe @stefank and @tschatzl remember this issue. Maybe it's ok for AIX, then removing this code is a good change. Maybe the G1 usages need a mmap implementation though.

The padding.inline.hpp usage seems to have one caller which is called once. The other mmap usage in G1 we can convert to mmap using a similar approach to zGranuleMap if that is preferred. That would then be equivalent behavior, it looks like the G1 code uses the page allocation granularity anyway so maybe keeping it mmap is the better way to go here anyway?

My uninformed opinion (I'm not the G1 code owner) is that it would be fine to use explicit mmap. I'd love the complexity reduction this patch brings.

Signed-off-by: Justin King <jcking@google.com>
@jcking
Copy link
Contributor Author

jcking commented Jan 13, 2023

/contributor add @stefank

@jcking
Copy link
Contributor Author

jcking commented Jan 13, 2023

Applied your patch @stefank

@openjdk
Copy link

openjdk bot commented Jan 13, 2023

@jcking
Contributor Stefan Karlsson <stefank@openjdk.org> successfully added.

Signed-off-by: Justin King <jcking@google.com>
Signed-off-by: Justin King <jcking@google.com>
@jcking
Copy link
Contributor Author

jcking commented Jan 13, 2023

Restored mmap for G1, though in the future it might make sense to switch to just using malloc.

Signed-off-by: Justin King <jcking@google.com>
@jcking
Copy link
Contributor Author

jcking commented Jan 19, 2023

There's a FIXME about that allocation.inline.hpp file is now empty. Will you remove it in this patch? I'd prefer if you did. Alternatively, remove the FIXME from this patch and then immediately clean this up as a separate patch.

Yeah, separate patch immediately after is probably best. Trying to avoid having to mass update includes and enter merge conflict hell for this patch. After is easier.

@backwaterred
Copy link
Contributor

backwaterred commented Jan 20, 2023

This leaves at least AIX as a potential problem. @backwaterred, does the AIX libc malloc() still exclusively use the data segment? Does free'd memory still stick to the process?

I agree with @coleenp that AIX seems to still use the data segment and sbrk for dynamic memory allocation according to the documentation. [Edit: I am not sure about memory sticking to the process. The section on load balancing seems to indicate that memory will indeed stick to the process unless the MALLOCOPTIONS variable is set to enable balancing.]

With these changes I see several of the gtests failing on AIX.

[==========] 58 tests from 6 test cases ran. (3718 ms total)
[  PASSED  ] 53 tests.
[  FAILED  ] 5 tests, listed below:
[  FAILED  ] os.safefetchN_negative_vm
[  FAILED  ] os.safefetch32_negative_vm
[  FAILED  ] os.safefetchN_negative_current_null_vm
[  FAILED  ] os.safefetch32_negative_current_null_vm
[  FAILED  ] os.safefetch_negative_at_safepoint_vm

I can experiment with the disclaim option as well.

[1] https://www.ibm.com/docs/en/aix/7.2?topic=concepts-system-memory-allocation-using-malloc-subsystem

@openjdk
Copy link

openjdk bot commented Feb 17, 2023

@jcking this pull request can not be integrated into master due to one or more merge conflicts. To resolve these merge conflicts and update this pull request you can run the following commands in the local repository for your personal fork:

git checkout remove-array-allocator
git fetch https://git.openjdk.org/jdk master
git merge FETCH_HEAD
# resolve conflicts and follow the instructions given by git merge
git commit -m "Merge master"
git push

@openjdk openjdk bot added the merge-conflict Pull request has merge conflict with target branch label Feb 17, 2023
@bridgekeeper
Copy link

bridgekeeper bot commented Mar 17, 2023

@jcking This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration!

@openjdk openjdk bot removed the rfr Pull request is ready for review label Apr 3, 2023
@bridgekeeper
Copy link

bridgekeeper bot commented Apr 15, 2023

@jcking This pull request has been inactive for more than 8 weeks and will now be automatically closed. If you would like to continue working on this pull request in the future, feel free to reopen it! This can be done using the /open pull request command.

@iklam
Copy link
Member

iklam commented May 22, 2023

@tstuefe @backwaterred I'd like to see this RFE revived. Do we know if anyone is using the ArrayAllocatorMallocLimit flag in any production environment today?

It seems unlikely to me, as you'd need to explicitly specify -XX:+UnlockExperimentalVMOptions in the command-line.

And, if this option had been useful (for the AIX port, for example), it would have been changed to a non-experimental (with proper _pd support) option over the past 10 years.


template <typename T>
size_t ZGranuleMap<T>::size_for_array(size_t count) {
return align_up(count * sizeof(T), os::vm_allocation_granularity());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert ag. overflow?


void* const addr = os::reserve_memory(size, !ExecMem, mtGC);
if (addr == nullptr) {
return nullptr;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reserve fails, we return null, commit fails, we exit? Why the inconsistency?

@@ -126,6 +123,33 @@ bool G1CMMarkStack::resize(size_t new_capacity) {
return true;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, and in ZGC: we are cool with small allocations being rounded up to page size now?

}

size_t G1CMMarkStack::size_for_array(size_t count) {
return align_up(count * sizeof(TaskQueueEntryChunk), os::vm_allocation_granularity());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert against overflow

// Clear the allocated memory.
memset(chunk, '\0', total_size);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Old code, when doing the malloc path, did not initialize. Why here?

@@ -35,7 +35,7 @@
import jdk.test.lib.Platform;

public class SizeTTest {
private static final String FLAG_NAME = "ArrayAllocatorMallocLimit";
private static final String FLAG_NAME = "StringDeduplicationCleanupDeadMinimum";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small comment here that the flag itself, does not matter, just the fact that it is of type size_t?

Copy link
Member

@tstuefe tstuefe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursory review

@tstuefe
Copy link
Member

tstuefe commented May 22, 2023

@tstuefe @backwaterred I'd like to see this RFE revived. Do we know if anyone is using the ArrayAllocatorMallocLimit flag in any production environment today?

It seems unlikely to me, as you'd need to explicitly specify -XX:+UnlockExperimentalVMOptions in the command-line.

And, if this option had been useful (for the AIX port, for example), it would have been changed to a non-experimental (with proper _pd support) option over the past 10 years.

@iklam I'm fine with removing the ArrayAllocator.

Copy link
Member

@stefank stefank left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we create a simpler version of this PR? One that only removes ArrayAllocator and ArrayAllocatorMallocLimit, but keeps MallocArrayAllocator and MmapArrayAllocator? That way we can figure out later, in a separate RFE, if we want to remove MmapArrayAllocator.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hotspot hotspot-dev@openjdk.org merge-conflict Pull request has merge conflict with target branch serviceability serviceability-dev@openjdk.org
Development

Successfully merging this pull request may close these issues.

6 participants