Skip to content

Commit 3318b1b

Browse files
authored
3.x: better leak detection via GC/sleep loop (#6628)
* 3.x: better leak detection via GC/sleep loop * Remove the unnecessary sleeps
1 parent a1693ec commit 3318b1b

File tree

3 files changed

+36
-43
lines changed

3 files changed

+36
-43
lines changed

src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRefCountTest.java

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -676,11 +676,7 @@ public Object call() throws Exception {
676676

677677
source.subscribe();
678678

679-
Thread.sleep(100);
680-
System.gc();
681-
Thread.sleep(100);
682-
683-
long after = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
679+
long after = TestHelper.awaitGC(GC_SLEEP_TIME, 20, start + 20 * 1000 * 1000);
684680

685681
source = null;
686682
assertTrue(String.format("%,3d -> %,3d%n", start, after), start + 20 * 1000 * 1000 > after);
@@ -712,11 +708,7 @@ public Object call() throws Exception {
712708
d1 = null;
713709
d2 = null;
714710

715-
Thread.sleep(100);
716-
System.gc();
717-
Thread.sleep(100);
718-
719-
long after = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
711+
long after = TestHelper.awaitGC(GC_SLEEP_TIME, 20, start + 20 * 1000 * 1000);
720712

721713
source = null;
722714
assertTrue(String.format("%,3d -> %,3d%n", start, after), start + 20 * 1000 * 1000 > after);
@@ -752,10 +744,7 @@ public Object call() throws Exception {
752744

753745
source.subscribe(Functions.emptyConsumer(), Functions.emptyConsumer());
754746

755-
System.gc();
756-
Thread.sleep(GC_SLEEP_TIME);
757-
758-
long after = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
747+
long after = TestHelper.awaitGC(GC_SLEEP_TIME, 20, start + 20 * 1000 * 1000);
759748

760749
source = null;
761750

@@ -787,10 +776,7 @@ public Object call() throws Exception {
787776
d1 = null;
788777
d2 = null;
789778

790-
System.gc();
791-
Thread.sleep(GC_SLEEP_TIME);
792-
793-
long after = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
779+
long after = TestHelper.awaitGC(GC_SLEEP_TIME, 20, start + 20 * 1000 * 1000);
794780

795781
source = null;
796782
assertTrue(String.format("%,3d -> %,3d%n", start, after), start + 20 * 1000 * 1000 > after);

src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCountTest.java

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -652,10 +652,7 @@ public Object call() throws Exception {
652652

653653
source.subscribe();
654654

655-
System.gc();
656-
Thread.sleep(100);
657-
658-
long after = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
655+
long after = TestHelper.awaitGC(GC_SLEEP_TIME, 20, start + 20 * 1000 * 1000);
659656

660657
source = null;
661658
assertTrue(String.format("%,3d -> %,3d%n", start, after), start + 20 * 1000 * 1000 > after);
@@ -686,10 +683,7 @@ public Object call() throws Exception {
686683
d1 = null;
687684
d2 = null;
688685

689-
System.gc();
690-
Thread.sleep(100);
691-
692-
long after = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
686+
long after = TestHelper.awaitGC(GC_SLEEP_TIME, 20, start + 20 * 1000 * 1000);
693687

694688
source = null;
695689
assertTrue(String.format("%,3d -> %,3d%n", start, after), start + 20 * 1000 * 1000 > after);
@@ -725,19 +719,7 @@ public Object call() throws Exception {
725719

726720
source.subscribe(Functions.emptyConsumer(), Functions.emptyConsumer());
727721

728-
long after = 0L;
729-
730-
for (int i = 0; i < 10; i++) {
731-
System.gc();
732-
733-
after = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
734-
735-
if (start + 20 * 1000 * 1000 > after) {
736-
break;
737-
}
738-
739-
Thread.sleep(GC_SLEEP_TIME);
740-
}
722+
long after = TestHelper.awaitGC(GC_SLEEP_TIME, 20, start + 20 * 1000 * 1000);
741723

742724
source = null;
743725
assertTrue(String.format("%,3d -> %,3d%n", start, after), start + 20 * 1000 * 1000 > after);
@@ -768,10 +750,7 @@ public Object call() throws Exception {
768750
d1 = null;
769751
d2 = null;
770752

771-
System.gc();
772-
Thread.sleep(GC_SLEEP_TIME);
773-
774-
long after = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
753+
long after = TestHelper.awaitGC(GC_SLEEP_TIME, 20, start + 20 * 1000 * 1000);
775754

776755
source = null;
777756
assertTrue(String.format("%,3d -> %,3d%n", start, after), start + 20 * 1000 * 1000 > after);

src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import static org.mockito.Mockito.mock;
1919

2020
import java.io.File;
21+
import java.lang.management.*;
2122
import java.lang.reflect.*;
2223
import java.net.URL;
2324
import java.util.*;
@@ -3391,4 +3392,31 @@ public Integer apply(Integer v) throws Throwable {
33913392
RxJavaPlugins.reset();
33923393
}
33933394
}
3395+
3396+
/**
3397+
* Repeatedly calls System.gc() and sleeps until the current memory usage
3398+
* is less than the given expected usage or the given number of wait loop/time
3399+
* has passed.
3400+
* @param oneSleep how many milliseconds to sleep after a GC.
3401+
* @param maxLoop the maximum number of GC/sleep calls.
3402+
* @param expectedMemoryUsage the memory usage in bytes at max
3403+
* @return the actual memory usage after the loop
3404+
* @throws InterruptedException if the sleep is interrupted
3405+
*/
3406+
public static long awaitGC(long oneSleep, int maxLoop, long expectedMemoryUsage) throws InterruptedException {
3407+
MemoryMXBean bean = ManagementFactory.getMemoryMXBean();
3408+
3409+
System.gc();
3410+
3411+
int i = maxLoop;
3412+
while (i-- != 0) {
3413+
long usage = bean.getHeapMemoryUsage().getUsed();
3414+
if (usage <= expectedMemoryUsage) {
3415+
return usage;
3416+
}
3417+
System.gc();
3418+
Thread.sleep(oneSleep);
3419+
}
3420+
return bean.getHeapMemoryUsage().getUsed();
3421+
}
33943422
}

0 commit comments

Comments
 (0)