我正在为我公司的项目编写单元测试。最近,我们的Jenkins构建在单元测试gradle任务期间开始失败,出现OutOfMemory
异常(由超出GC overhead
限制或Java heap space
引起)。在我的本地机器单元上进行测试,但有时甚至使用3.5 GB的内存。项目中有超过1000个测试,其中数百个测试创建Activity
实例。
我已经进行了堆转储(在大约1000次测试通过的时刻)并使用VisualVM进行了检查。由于内存使用量和堆大小都在不断增加,我怀疑某处发生了内存泄漏。
我使用堆转储运行Eclipse Memory Analyzer,#1泄漏可疑是非常多ReceiverDispatcher
个实例,大约255k个(使用> 500 MB内存)。此外,堆包含相同数量的IntentReceiverLeaked
个实例,稍多(258k)shadowBroadcastReceiver
个实例,以及两倍(约127k)AccessibilityManagerService
和LockPatternUtils
个实例。
这对我来说很奇怪,因为我发现只有一个BroadcastReceiver
在项目中动态注册(MainActivity
),并且它在Activity的onDestroy()
方法中正确注销。
由于转储中有大约300个Activity实例,我怀疑泄漏活动是导致泄漏的主要原因。此外,Eclipse Memory Analyzer将ShadowContextImpl
列为#2问题嫌疑人,实例略多于活动(但使用的内存大约为500 MB)。
使用Robolectric.setupActivity()
创建测试中的活动,并在tearDown()
上调用相关的生命周期方法
//Android Annotations are in use
protected ActivityController<MainActivity_> activityController;
protected MainActivity_ mainActivity;
@Before
public void setUp() throws Exception {
activityController = Robolectric.buildActivity(MainActivity_.class).setup();
mainActivity = activityController.get();
}
@After
public void tearDown() throws Exception {
try {
if (activityController != null) {
activityController.pause()
.stop()
.destroy();
}
} catch (Throwable th){}
mainActivity = null;
activityController = null;
}
对于与Fragment相关的测试,Fragment以常规方式创建并附加到Activity(而不是使用FragmentTestUtil)
public static void startFragment(Fragment fragment, FragmentActivity activity) {
shadowOf(Looper.getMainLooper()).pause();
FragmentManager fragmentManager = activity.getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(fragment, null);
fragmentTransaction.commitAllowingStateLoss();
shadowOf(Looper.getMainLooper()).runToEndOfTasks();
}
(...)
Activity activity = Robolectric.setupActivity(MainActivity_.class);
MyFragment fragment = MyFragment_.builder().build();
startFragment(fragment, activity);
(...)
这些泄漏的原因(以及如何修复)可能是什么原因?活动和碎片的启动方式是否与泄漏有关?
答案 0 :(得分:0)
对于它的价值-在尝试将我们的应用程序迁移到RoboElectric 4.3和AndroidX时,我一直在尝试修复OOM错误-在进行并重构2000测试以确保它们完成并处置了我可以观看的片段和活动之前VisualVM中的gradleRunner进程会最大程度地利用它的可用堆,并且测试运行的速度越来越慢,直到它们停止或因OOM错误而崩溃
这使我重新工作,将其添加到应用程序build.grade中:
testOptions {
unitTests {
all {
maxParallelForks = Runtime.runtime.availableProcessors()
forkEvery = 100
}
}
}
意味着在一个进程中将仅运行100个测试,因此它们永远不会达到堆大小。