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

Await in loops cause memory leak #56

Open
zekronium opened this issue Feb 22, 2021 · 5 comments
Open

Await in loops cause memory leak #56

zekronium opened this issue Feb 22, 2021 · 5 comments

Comments

@zekronium
Copy link

Hi,

I am reporting this observation that came up when we noticed our instances getting OOM killed.

This happens in any sort of loop, if the future completes instantly, no issue, but if it enters the synthetic method that the instrumentation creates, all the completable futures will not be GC'd till that method exits.

If you have this loop logic in one method and another method awaits on it, the problem persists, but now in the "awaiting" method.

If you lets say change infinite loops to limited for's, now the same "build-up" of futures will happen on the method that awaits on the looping method to complete (if that awaiting method is looping/retrying itself).

Recursive calls, separate thread completion and so on all retain the same issue.

Is there a way to attempt fixing this, I am willing to contribute but I lack experience on this specific instrumentation topic.

Thank you.

@dwing4g
Copy link

dwing4g commented Feb 24, 2021

I also encountered this problem before.
In order to fix it, I had to extend CompletableFuture and reimplement thenCompose() and complete().

@zekronium
Copy link
Author

zekronium commented Feb 24, 2021

I also encountered this problem before.
In order to fix it, I had to extend CompletableFuture and reimplement thenCompose() and complete().

What changes did you have to make?

@silence-coding
Copy link

How to solve the problem

@silence-coding
Copy link

Original Code:

    static CompletableFuture<String> convert() {
        for (int i = 0; i < 2; i++) {
            CompletableFuture<Integer> e = CompletableFuture.completedFuture(1);
            Async.await(e);
        }
        return CompletableFuture.completedFuture("ok");
    }

@silence-coding
Copy link

Generated after EA:

static CompletableFuture<String> convert() {
    for (int i = 0; i < 2; i++) {
      CompletableFuture<Integer> e = CompletableFuture.completedFuture(Integer.valueOf(1));
      if (!e.isDone()) { CompletableFuture<Integer> completableFuture = e; return completableFuture.exceptionally((Function)Function.identity()).thenCompose(paramObject -> { // Byte code:
              //   0: iload_3
              //   1: tableswitch default -> 90, 0 -> 24, 1 -> 86
              //   24: iconst_0
              //   25: istore_0
              //   26: iload_0
              //   27: iconst_2
              //   28: if_icmpge -> 80
              //   31: iconst_1
              //   32: invokestatic valueOf : (I)Ljava/lang/Integer;
              //   35: invokestatic completedFuture : (Ljava/lang/Object;)Ljava/util/concurrent/CompletableFuture;
              //   38: astore_1
              //   39: aload_1
              //   40: dup
              //   41: invokevirtual isDone : ()Z
              //   44: ifne -> 70
              //   47: astore_2
              //   48: aload_2
              //   49: invokestatic identity : ()Ljava/util/function/Function;
              //   52: invokevirtual exceptionally : (Ljava/util/function/Function;)Ljava/util/concurrent/CompletableFuture;
              //   55: iload_0
              //   56: aload_1
              //   57: aload_2
              //   58: sipush #1
              //   61: <illegal opcode> apply : (ILjava/util/concurrent/CompletableFuture;Ljava/util/concurrent/CompletableFuture;I)Ljava/util/function/Function;
              //   66: invokevirtual thenCompose : (Ljava/util/function/Function;)Ljava/util/concurrent/CompletableFuture;
              //   69: areturn
              //   70: invokevirtual join : ()Ljava/lang/Object;
              //   73: pop
              //   74: iinc #0, 1
              //   77: goto -> 26
              //   80: ldc 'ok'
              //   82: invokestatic completedFuture : (Ljava/lang/Object;)Ljava/util/concurrent/CompletableFuture;
              //   85: areturn
              //   86: aload_2
              //   87: goto -> 70
              //   90: new java/lang/IllegalArgumentException
              //   93: dup
              //   94: invokespecial <init> : ()V
              //   97: athrow
              // Line number table:
              //   Java source line number -> byte code offset
              //   #17	-> 24
              //   #18	-> 31
              //   #19	-> 39
              //   #17	-> 74
              //   #21	-> 80
              // Local variable table:
              //   start	length	slot	name	descriptor
              //   39	35	1	e	Ljava/util/concurrent/CompletableFuture;
              //   26	54	0	i	I
              // Local variable type table:
              //   start	length	slot	name	signature
              //   39	35	1	e	Ljava/util/concurrent/CompletableFuture<Ljava/lang/Integer;>; }); }  e.join();
    } 
    return CompletableFuture.completedFuture("ok");
  }

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