RealmError: Realm Out of memory

I am using Realm 3.0.0 as my android app database. It's like a questionnaire app, where the user moves around a lot within the app. When I use the application constantly (going back and forth) I get the following error:

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)

      

Now I know that the main reason for this is not closing the Realm instances. But I have already tested this several times. And I'm pretty sure I close all open instances.

There are many actions and fragments in the application that get a Realm instance on their own onCreate

and close it on onDestroy

. There are also other background networking jobs that run to load data that Realm instances receive. These jobs close their Realm instances when they have finished or when they have canceled.

All of the above gets their Realm instance through dagger injection 2:

  @Provides
  @Nullable
  static Realm realm(@Nullable RealmConfiguration configuration) {
    if (configuration != null) {
      Realm.setDefaultConfiguration(configuration);
      return Realm.getDefaultInstance();
    }
    return null;
  }

      

The configuration is also provided in the same dagger module.

To be more specific, the questionnaire consists of many question fragments displayed in the ViewPager. Each fragment is entered with an area. Many interactions in a given fragment of a Question write data to the database (some asynchronous, some blocking). These snippets also query the database onResume

to get their updated data. Some of this data is also copied from the Kingdom via realm.copyFromRealm()

. Now, at any given time, the download execution is most likely done and reads data from the DB and uploads it to the server. When the download job completes, it is then written to the database.

I think I can have up to 7-12 Fragments / Activities containing a region reference in the UI thread at the moment. And 0-6 other links to 0-3 other threads (background jobs).

Also, I am shrinking my real DB across Realm.compactRealm(realmConfiguration)

every time I start the application (perhaps as a separate issue, this doesn't seem to make it work stably).

Above, I tried to describe my use of Realm in a descriptive way without going into details. Now my problem is that the user is overusing the application (moving back and forth between activities / fragments (realtime query + DB read query), loading data (realtime query + DB read and write query)), I getting the aforementioned laid out memory errors.

I also use Leak Canary and it didn't find any leaks. (Not sure if this could do it anyway)

Am I using the Kingdom in a way that it should not be used? Should I close Realm instances onPause

instead onDestroy

? Should I only have one instance of the object in action and have all of its fragmetns (up to 5 in my case), use this instance? What changes can I make to my application and possibly my application architecture to solve this problem?

I appreciate any help in trying to resolve this issue.

EDIT: I am sharing open-close logic in my background.

All my assignments share the same use of the area, namely: Kingdom is entered lazily via:

@Inject protected transient Lazy<Realm> lazyRealm;

The region object reference is stored in the field private transient Realm realm;

. I am using Android Priority Work Queue . When the task is added:

@Override
public void onAdded() {
    realm = lazyRealm.get();
    realm.executeTransaction(realm1 -> {
        //write some stuff into realm
    });
    realm.close();
}

      

And when the job starts, it is deleted once and every possible end of this method has a call 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();
    }
  }

      

As you can see, these background threads are closing their scope instances correctly as far as I know.

+3


source to share


1 answer


While it is true that all of my background tasks were realm.close()

calling, one of them called it too late in this lifecycle. This was my GPSService, which is a background service. The problem was that the GPS service is initialized when the app is run as an Android service, which rarely gets destroyed. I injected a region instance onCreate

and closed it onDestroy

. After @EpicPandaForce comments and reading his articles about using the scope correctly. I realized that this was the cause of the leak. The looper thread maintained a link to the open area for a very long time, so it mmap

inflated every time a write transaction occurs. Now when I moved the get / close area, every time the service starts, my problem is fixed.



My trick is to handle the access to the background thread area very carefully. Thank you for your quick answers and help!

+2


source







All Articles