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

Fix MpscLinkedQueue GC issues #7799

Open
wants to merge 5 commits into
base: 3.x
Choose a base branch
from

Commits on Nov 21, 2024

  1. Refactor MpscLinkedQueue.poll

    Handle empty queue first, then share most of the
    implementation for non-empty scenarios (spin and
    non-spin).
    olivergillespie committed Nov 21, 2024
    Configuration menu
    Copy the full SHA
    b8582b9 View commit details
    Browse the repository at this point in the history
  2. Unlink dead nodes in MpscLinkedQueue

    Similar to
    https://github.com/JCTools/JCTools/blob/master/jctools-core/src/main/java/org/jctools/queues/MpscLinkedQueue.java#L120,
    null out the next pointer in the discarded consumer node
    when polling from the queue. If not, we leave behind a (potentially long)
    chain of connected garbage nodes. If we're unlucky (for example one of
    the early nodes is promoted to old generation, triggering nepotism),
    this can cause GC issues as now we have a long linked list which must be
    marked by young collections.
    
    Reproducer:
    
    ```
    import io.reactivex.rxjava3.internal.queue.MpscLinkedQueue;
    
    public class MpscLinkedQueueGC {
        public static void main(String[] args) {
            MpscLinkedQueue<Integer> queue = new MpscLinkedQueue<>();
            for (int i = 0; i < 10; i++) System.gc(); // tenure consumer node
            while (true) {
                queue.offer(123);
                queue.poll();
            }
        }
    }
    ```
    
    ```
    Before fix:
    
    $ java -Xlog:gc -Xmx1G -cp build/classes/java/main MpscLinkedQueueGC.java
    ...
    [1.261s] GC(20) Pause Young (Normal) (G1 Preventive Collection) 115M->115M(204M) 209.335ms
    [1.385s] GC(23) Pause Young (Normal) (G1 Evacuation Pause) 148M->149M(204M) 31.491ms
    [1.417s] GC(24) Pause Young (Normal) (G1 Evacuation Pause) 157M->158M(204M) 19.333ms
    [1.453s] GC(25) Pause Young (Normal) (G1 Evacuation Pause) 166M->167M(599M) 22.678ms
    [1.966s] GC(26) Pause Young (Normal) (G1 Evacuation Pause) 249M->249M(497M) 305.238ms
    ...
    
    After fix:
    $ java -Xlog:gc -Xmx1G -cp build/classes/java/main MpscLinkedQueueGC.java
    ...
    [1.169s] GC(14) Pause Young (Normal) (G1 Evacuation Pause) 304M->2M(506M) 0.755ms
    [1.558s] GC(15) Pause Young (Normal) (G1 Evacuation Pause) 304M->2M(506M) 0.689ms
    [1.948s] GC(16) Pause Young (Normal) (G1 Evacuation Pause) 304M->2M(506M) 0.800ms
    [2.337s] GC(17) Pause Young (Normal) (G1 Evacuation Pause) 304M->2M(506M) 0.714ms
    ...
    ```
    olivergillespie committed Nov 21, 2024
    Configuration menu
    Copy the full SHA
    4c6c6fa View commit details
    Browse the repository at this point in the history
  3. Revert "Unlink dead nodes in MpscLinkedQueue"

    This reverts commit 4c6c6fa.
    olivergillespie committed Nov 21, 2024
    Configuration menu
    Copy the full SHA
    43a7cf7 View commit details
    Browse the repository at this point in the history
  4. Revert "Refactor MpscLinkedQueue.poll"

    This reverts commit b8582b9.
    olivergillespie committed Nov 21, 2024
    Configuration menu
    Copy the full SHA
    d51d3ff View commit details
    Browse the repository at this point in the history
  5. Unlink dead nodes in MpscLinkedQueue

    Similar to
    https://github.com/JCTools/JCTools/blob/master/jctools-core/src/main/java/org/jctools/queues/MpscLinkedQueue.java#L120,
    null out the next pointer in the discarded consumer node
    when polling from the queue. If not, we leave behind a (potentially long)
    chain of connected garbage nodes. If we're unlucky (for example one of
    the early nodes is promoted to old generation, triggering nepotism),
    this can cause GC issues as now we have a long linked list which must be
    marked by young collections.
    
    Reproducer:
    
    ```
    import io.reactivex.rxjava3.internal.queue.MpscLinkedQueue;
    
    public class MpscLinkedQueueGC {
        public static void main(String[] args) {
            MpscLinkedQueue<Integer> queue = new MpscLinkedQueue<>();
            for (int i = 0; i < 10; i++) System.gc(); // tenure consumer node
            while (true) {
                queue.offer(123);
                queue.poll();
            }
        }
    }
    ```
    
    ```
    Before fix:
    
    $ java -Xlog:gc -Xmx1G -cp build/classes/java/main MpscLinkedQueueGC.java
    ...
    [1.261s] GC(20) Pause Young (Normal) (G1 Preventive Collection) 115M->115M(204M) 209.335ms
    [1.385s] GC(23) Pause Young (Normal) (G1 Evacuation Pause) 148M->149M(204M) 31.491ms
    [1.417s] GC(24) Pause Young (Normal) (G1 Evacuation Pause) 157M->158M(204M) 19.333ms
    [1.453s] GC(25) Pause Young (Normal) (G1 Evacuation Pause) 166M->167M(599M) 22.678ms
    [1.966s] GC(26) Pause Young (Normal) (G1 Evacuation Pause) 249M->249M(497M) 305.238ms
    ...
    
    After fix:
    $ java -Xlog:gc -Xmx1G -cp build/classes/java/main MpscLinkedQueueGC.java
    ...
    [1.169s] GC(14) Pause Young (Normal) (G1 Evacuation Pause) 304M->2M(506M) 0.755ms
    [1.558s] GC(15) Pause Young (Normal) (G1 Evacuation Pause) 304M->2M(506M) 0.689ms
    [1.948s] GC(16) Pause Young (Normal) (G1 Evacuation Pause) 304M->2M(506M) 0.800ms
    [2.337s] GC(17) Pause Young (Normal) (G1 Evacuation Pause) 304M->2M(506M) 0.714ms
    ...
    ```
    olivergillespie committed Nov 21, 2024
    Configuration menu
    Copy the full SHA
    898c19d View commit details
    Browse the repository at this point in the history