JDK-8352765 : G1CollectedHeap::expand_and_allocate() may fail to allocate even after heap expansion succeeds
  • Type: Bug
  • Component: hotspot
  • Sub-Component: gc
  • Affected Version: 21,24,25
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2025-03-24
  • Updated: 2025-03-31
  • Resolved: 2025-03-31
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.
JDK 25
25 masterFixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
This problem was discovered during the work for SoftMaxHeapSize (JDK-8236073) (and our internal ProposedHeapSize). In  G1CollectedHeap::expand_and_allocate(), it does:

if (expand(expand_bytes, _workers)) {
    ...
    return attempt_allocation_at_safepoint(word_size, false);
}

We found that when expand() returns true, attempt_allocation_at_safepoint() could immediately return nullptr. This is counter-intuitive, because allocation immediately after a heap expansion should normally succeed. One possible cause is another thread immediately uses up the newly created region, because expand() and attempt_allocation_at_safepoint() are not atomic.

This mostly affects use cases for SoftMaxHeapSize, where setting a small SoftMaxHeapSize could cause OutOfMemoryError. To work around it, we internally revived the removed attempt_allocation_force() method (deleted by JDK-8331562), and use it when this problem happens.
Comments
Changeset: 804a8fa4 Branch: master Author: Ivan Walulya <iwalulya@openjdk.org> Date: 2025-03-31 11:15:47 +0000 URL: https://git.openjdk.org/jdk/commit/804a8fa4e96a32be2b38c2d0906dc5861a292007
31-03-2025

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk/pull/24257 Date: 2025-03-26 15:05:46 +0000
26-03-2025

Thank you both for the quick response. Yes, I confirm [~iwalulya]'s https://github.com/openjdk/jdk/compare/master...walulyai:jdk:ReceivingYoung fixes this problem. So this issue was probably caused by JDK-8253413. A simple repro is to patch https://github.com/openjdk/jdk/pull/24211 , then run with -XX:MinHeapSize=8m -XX:SoftMaxHeapSize=8m for any application with a reasonably large live size. I will add a jtreg test in https://github.com/openjdk/jdk/pull/24211 , to ensure setting a small SoftMaxHeapSize won't trigger OutOfMemoryError.
25-03-2025

[~manc] can you please try https://github.com/openjdk/jdk/compare/master...walulyai:jdk:ReceivingYoung?expand=1, see if it solves the issue.
25-03-2025

We have been working on this since last week. Should send out a PR later today. The problem is that after the collection, there are no Eden regions provisioned for. So the Mutator successfully expands the heap, but fails to allocate because of the G1Policy's target length for the young regions is already met.
25-03-2025

Fwiw, that code snippet: if (expand(expand_bytes, _workers)) { ... return attempt_allocation_at_safepoint(word_size, false); } is always run single-threaded on the VM thread (see the assert_at_safepoint_on_vm_thread(); statement above), so there can be no other threads can steal that allocation afaict. Not sure if this is the problem, but there is a known issue with expand_heap_after_young_collection() which does not take into account how much memory the mutator requested, and may not expand although there are no free regions left for eden, so there will be a full gc following right a young gc.
25-03-2025

We did not notice this problem in JDK 11, but noticed it in JDK 21. G1CollectedHeap::expand_and_allocate() looks the same in JDK 11 and 21. I'll dig more into the root cause. If anyone has suggestions, feel free to comment.
24-03-2025