2017-03-31 158 views
3

我使用Realm 3.0.0作爲我的Android應用程序的數據庫。這就像一個問卷調查應用程序,用戶在應用程序內導航很多。當我使用的應用程序(來回)不斷,我得到以下錯誤:RealmError:Realm內存不足

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) 

現在我知道這樣做的主要原因是不打烊領域實例。但我已經多次檢查過。我確信我關閉了每一個我打開的事件。

該應用程序有許多活動和片段,它們都在onCreate上獲得Realm實例,並在onDestroy上關閉它。還有其他後臺網絡作業可用於上載獲取Realm實例的數據。這些作業在完成運行或取消時會關閉它們的Realm實例。

所有上述的經由匕首2得到他們的領域實例直通注射:

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

配置也以相同的匕首模塊提供。

更具體地說,調查問卷包含ViewPager中顯示的很多問題片段。每個片段被注入一個領域。給定Question Fragment中的許多交互將數據寫入數據庫(某些異步,某些阻止)。這些片段還會查詢onResume上的數據庫以獲取其更新的數據。其中一些數據也通過realm.copyFromRealm()從Realm中複製出來。現在,在這些情況發生的任何時候,上傳作業最有可能運行,並從數據庫讀取數據並將其上傳到服務器。上傳作業完成後,它會寫入數據庫。

我想我可以有多達7-12片段/活動在給定時刻在UI線程上持有領域引用。以及0-3其他線程(Background Jobs)上的其他參考。

此外,我在每次應用程序發佈時通過Realm.compactRealm(realmConfiguration)壓縮我的領域數據庫(可能作爲一個單獨的問題,這似乎並不能完成它的工作)。

上面我試圖用描述性的描述我的領域用法,而沒有深入細節。現在我的問題是,當用戶過度使用應用程序(在活動/片段之間來回切換(realm注入+數據庫讀取查詢),上傳數據(realm注入+數據庫讀取&寫入查詢)),我得到以上貼出內存錯誤。

我也在使用泄漏金絲雀,它沒有檢測到任何泄漏。 (不知道它是否可以)

我是否以不應該使用Realm的方式?我應該關閉Realm實例而不是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(); 
} 

和作業程序運行時的境界是retreived一次,這種方法的每一種可能的結局有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(); 
    } 
    } 

通話正如你可以看到,這些背景線程確實關閉他們的領域實例,就我而言。

+0

'和0-3其他線程(背景作業)的其他參考.'我很確定「後臺作業」不關閉他們的領域。 – EpicPandaForce

+0

@EpicPandaForce他們關閉了他們的領域。我編輯了我的問題,並添加了我的後臺作業的領域開放/關閉邏輯。 –

+0

@EpicPandaForce我想認爲我不那麼愚蠢。我已經有了這個問題超過2個星期,並一直試圖解決它。確保我所有領域的實例都關閉是我做的第一件事。我必須在更大規模上做錯事。 –

回答

2

雖然我所有的後臺任務都確實調用了realm.close(),但其中一個人在生命週期中稱它爲時已晚。那是我的GPSService,這是一個後臺服務。問題在於GPS服務在應用程序作爲Android服務啓動時初始化,這很少被破壞。我正在注入領域實例onCreate並關閉它onDestroy。在@EpicPandaForce的評論之後,閱讀他關於正確使用領域的文章。我意識到這是泄漏的原因。一個非循環線程在很長的時間內保持開放的領域參考,因此,每次發生寫事務時mmap都在膨脹。現在,每當服務運行時,我都會將領域的接近/移近發生,我的問題已得到解決。

我帶走的是一個需要非常精細地對待後臺線程領域訪問。謝謝你們的快速反應和幫助!