我使用 Realm 3.0.0 作为我的 Android 应用程序的数据库。这就像一个问卷调查应用程序,用户在应用程序内部进行了很多导航。当我连续使用该应用程序(来回)时,出现以下错误:
Fatal Exception: io.realm.exceptions.RealmError: Unrecoverable error. mmap() failed: Out of memory size: 1073741824 offset: 0 in /Users/cm/Realm/realm-java/realm/realm-library/src/main/cpp/io_realm_internal_SharedRealm.cpp line 109
at io.realm.internal.SharedRealm.nativeGetSharedRealm(SharedRealm.java)
at io.realm.internal.SharedRealm.(SharedRealm.java:187)
at io.realm.internal.SharedRealm.getInstance(SharedRealm.java:229)
at io.realm.internal.SharedRealm.getInstance(SharedRealm.java:204)
at io.realm.RealmCache.createRealmOrGetFromCache(RealmCache.java:124)
at io.realm.Realm.getDefaultInstance(Realm.java:210)
现在我知道造成这种情况的主要原因是没有关闭 Realm 实例。但我已经检查过很多次了。我很肯定我会关闭我打开的每个实例。
该应用程序有许多 Activity 和 Fragment,它们都在其上获取 Realm 实例onCreate
并在其onDestroy
. 还有其他后台网络作业运行以上传获取 Realm 实例的数据。这些作业在完成运行或取消时关闭其 Realm 实例。
以上所有内容都是通过 Dagger 2 通过注入获得他们的 Realm 实例:
@Provides
@Nullable
static Realm realm(@Nullable RealmConfiguration configuration) {
if (configuration != null) {
Realm.setDefaultConfiguration(configuration);
return Realm.getDefaultInstance();
}
return null;
}
配置也在同一个 Dagger 模块中提供。
更具体地说,问卷由 ViewPager 中显示的许多问题片段组成。每个片段都被注入一个领域。给定问题片段中的许多交互将数据写入数据库(一些异步,一些阻塞)。这些片段还查询数据库onResume
以获取其更新的数据。其中一些数据也通过realm.copyFromRealm()
. 现在,在这些发生的任何给定时间,上传作业很可能正在运行并从数据库读取数据并将其上传到服务器。上传作业完成后,它会写入数据库。
我认为在给定时刻,我最多可以有 7-12 个片段/活动在 UI 线程上持有领域引用。以及 0-3 个其他线程(后台作业)上的 0-6 个其他引用。
此外,我在每次应用程序启动时压缩我的领域数据库Realm.compactRealm(realmConfiguration)
(也许作为一个单独的问题,这似乎并没有始终如一地完成它的工作)。
上面我试图描述性地描述我的 Realm 用法,而没有详细说明。现在我的问题是,当用户过度使用应用程序时(在活动/片段之间来回切换(领域注入 + DB 读取查询),上传数据(领域注入 + DB 读写查询)),我得到上面发布的内存不足错误。
我也在使用 Leak Canary,它没有检测到任何泄漏。(不确定是否可以)
我是否以不应该使用的方式使用 Realm?我应该关闭 Realm 实例onPause
而不是onDestroy
?我是否应该在一个活动中只有一个领域实例,并让它的所有片段(在我的情况下最多 5 个)都使用这个实例?我可以在我的应用程序中进行哪些更改,也许我的应用程序架构可以解决这个问题?
感谢您尝试解决此问题的任何帮助。
编辑:我在后台线程中共享领域打开关闭逻辑。
我所有的工作共享相同的领域使用情况,如下所示:领域是通过以下方式惰性注入的:
@Inject protected transient Lazy<Realm> lazyRealm;
领域对象引用保存在private transient Realm realm;
字段中。我正在使用Android Priority Job Queue。添加作业时:
@Override
public void onAdded() {
realm = lazyRealm.get();
realm.executeTransaction(realm1 -> {
//write some stuff into realm
});
realm.close();
}
并且当作业运行时,领域会被检索一次,并且此方法的每个可能的结束都调用realm.close()
@Override public void onRun() throws Throwable {
synchronized (syncAdapterLock) {
realm = lazyRealm.get();
Answer answer = realm.where(Answer.class).equalTo(AnswerQuery.ID, answerId).findFirst();
if (answer == null) {
realm.close();
throw new RealmException("File not found");
}
final File photoFile = new File(answer.getFilePath());
final Response response = answerService.uploadPhotoAnswer(answerId, RequestBody.create(MediaType.parse("multipart/form-data"), photoFile)).execute();
if (!response.isSuccessful()) {
realm.close();
throw new HttpError(statusCode);
}
realm.executeTransaction(realm1 -> {
answer.setSyncStatus(SyncStatus.SYNCED.getCode());
});
}
realm.close();
}
}
如您所见,就我而言,这些后台线程确实正确关闭了它们的领域实例。