RealmError:Realm内存不足

时间:2017-03-31 15:07:58

标签: android realm realm-java

我使用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实例。但是我已经多次检查过了。而且我很肯定我关闭了我打开的所有实例。

该应用包含许多活动和片段,所有这些活动和片段都会在onCreate上获得Realm实例并在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()从Realm中复制出来。现在,在发生这些情况的任何给定时间,上传作业很可能正在运行并从数据库读取数据并将其上载到服务器。当上载作业完成时,它会写入数据库。

我想我可以在给定时刻在UI线程上拥有最多7-12个片段/活动。 0-3其他线程上的0-6个其他引用(后台作业)。

此外,我在每个应用启动时通过Realm.compactRealm(realmConfiguration)压缩我的领域数据库(可能作为一个单独的问题,这似乎并不总是这样做)。

上面我试图描述性地描述我的Realm用法,而不是详细说明。现在我的问题是,当用户过度使用应用程序(在活动/片段之间往返(领域注入+数据库读取查询),上传数据(领域注入+数据库读取和写入查询)),我得到上面发布的内存错误。

我也在使用泄漏金丝雀,但它没有检测到任何泄漏。 (不确定它是否可以)

我是否以某种不应该使用Realm的方式使用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();
    }
  }

正如您所看到的,就我而言,这些后台主题确实正确地关闭了它们的领域实例。

1 个答案:

答案 0 :(得分:7)

虽然我的所有后台任务都确实调用了realm.close(),但其中一个人在其生命周期中称它为时已晚。那是我的GPSService,这是一个后台服务。问题是GPS服务在作为Android服务推出时初始化,很少被销毁。我正在注入一个领域实例onCreate并关闭它onDestroy。在@EpicPandaForce的评论之后,阅读他关于正确使用领域的文章。我意识到这是泄漏的原因。非looper线程在很长一段时间内都保持一个开放的域引用,因此,每次写入事务发生时mmap都会膨胀。现在我每次运行服务时都移动了领域get / close,我的问题就解决了。

我的看法是,需要非常精细地处理后台线程域访问。感谢您的快速回复和帮助!