I am getting memory exception anonymously, i don't know which block of code causes it. but i know its from realm. Is it because i'm using single insert instead of bulk insert.
realm.beginTransaction();
summary = realm.createObject(ActivitySummary.class);
JSONObject activity = activities.getJSONObject(i);
JSONArray datas;
summary.setActID(activity.getInt("actID"));
summary.setActName(activity.getString("actName"));
summary.setSourceID(activity.getString("sourceID"));
datas = activity.getJSONArray("data");
for (int j = 0; j < datas.length(); j++) {
JSONObject data = datas.getJSONObject(j);
Date endTime = !data.getString("endTime").equals("null") ? new Date(data.getLong("endTime")) : new Date();
summary.setEndTime(endTime);
summary.setLogID(data.getString("logID"));
summary.setDate(data.getInt("date"));
summary.setStartTime(!data.getString("endTime").equals("null") ? new Date(data.getLong("startTime")) : new Date());
summary.setValue(data.getString("value1"));
summary.setValue2(data.getString("value2"));
realm.commitTransaction();
}
Exception 12-23 10:40:10.302: A/libc(13101): Fatal signal 11 (SIGSEGV) at 0xd1d1d1cd (code=1), thread 13101 (com.lifestyle) –
You definitely need to close your Realm instances by calling realm.close()
If you are using Realm on multiple threads, than you might have stumbled on Realm.io bug where it was sending update notification messages to dead threads/Realms.
It had been fixed in version 0.76.0 by adding this piece of code in close() method:
if (handler != null) {
handlers.remove(handler);
}
Updating to newest version fixed this same error for me.
You need to keep track of all your Realm.getInstance() and realm.close() - they have to be called exactly same number of times for each thread. Even though the Realm object itself is only one per thread, calling getInstance() iterates it's ref counter by one, and close() decreases ref counter by one. Realm will be dismissed when ref counter gets to 0.
There is a bug in the above code. A beginTransaction() must have a matching commitTransaction() and vice versa. It is recommended to use executeTransaction() to ensure that. So you either have to move the beginTransaction() inside the loop or the commitTransaction() outside the loop. It's not entirely clear if that's your memory problem though.
Related
What is best for memory and performance !
I have some data came from fire base through a listener, should i put them on a list of object using for loop then insert the list to the room or make a for loop and looping the (insert to room) !!!
Method 1 :
List<object> list = new List<>.
();
For (int i;i>= list.size; i++){
list.add(data);
}
InsertToRoom(list);
Method 2:
For (int i;i>= list.size; i++){
InsertToRoom(name, age)
}
I would recommend you to use only Method 1.
Because inside "Method 1" you are calling the expensive databse operation InsertToRoom() only once.
Whereas, inside "Method 2" you are calling the expensive database operations multiple times, i.e. the InsertToRoom() operation will be called multiple times until the for loop gets completed. This is not performant and efficient.
As a Good Android Citizen you should restrict multiple calls to database, unless genuinely required.
Hence, please go with Method 1.
I'm seeing a very weird problem with getting the count from a query not running on the main thread. I have a UI that uses the RecyclerView adapter for Realm, and it works just dandy.
I have a method that counts the number of records before a query so it can set a starting point, for some reason it will return the previous count was before the last transaction. Here is a somewhat shortened version with the output of the log:
D/SearchController: Query Count (Main Thread): 50
D/SearchController: Query Count (Rx Thread): 50
D/SearchController: Query Count (Main Thread): 100
D/SearchController: Query Count (Rx Thread): 50
public Single<Update> searchWithCriteriaForMore(SearchCriteria searchCriteria) {
Realm realmI = Realm.getDefaultInstance();
Timber.d("Query Count (Main Thread): %d", realmI.where(Ad.class).equalTo("searchId", searchCriteria.id()).count());
realmI.close();
return Single.defer(() -> {
Realm realm = Realm.getDefaultInstance();
Timber.d("Query Count (Rx Thread): %d", realm.where(Ad.class).equalTo("searchId", searchCriteria.id()).count());
realm.close();
// Stuff to add records on Rx Thread
});
}
The call looks like this:
SearchController.instance().searchWithCriteriaForMore(searchCriteria)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleSubscriber<SearchController.Update>() {
...
}
Thoughts anyone? I've been trying to nail this down for a few weeks, thought it was something I was doing, but if I do the count on the main thread and pass it in, it works fine.
I'm running RxJava 1.2.7, RxAndroid 1.2.1, and Realm 3.3.1.
It feels that you have a Realm instance on RxThread somewhere which is not closed before. Since RxThread doesn't have a looper, the realm instance cannot be auto-updated. So it was locked at the version when it was created.
Realm is using ref counter internally, the getDefaultInstance() will just return the same instance you opened on the RxThread before.
The solution:
1. Find out which Realm instance was retrieved on the RxThread and close it properly. So the next time getDefaultInstance() will return a new Realm instance at the latest version data.
2. If it is intended to have a Realm instance on the RxThread, you can call realm.refresh() to manually move it to the latest data version.
I am using Realm 3.0.0 as the DB of my Android app. It's like a questionnaire application, in which the user navigates inside the app a lot. When I use the app (go back and forth) continuously, 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 the main cause of this is not closing Realm instances. But I've already checked for that multiple times. And I am positive that I close every instance I open.
The app has many activities and fragments that all get a Realm instance on their onCreate and close it on their onDestroy. There are also other background network jobs that run to upload data that get Realm instances. These jobs close their Realm instances when they've finished running or when they cancel.
All of the above get their Realm instance thru injection via Dagger 2:
#Provides
#Nullable
static Realm realm(#Nullable RealmConfiguration configuration) {
if (configuration != null) {
Realm.setDefaultConfiguration(configuration);
return Realm.getDefaultInstance();
}
return null;
}
Configuration is also provided in the same Dagger Module.
To be more specific, a Questionnaire consists of many Question Fragments displayed in a ViewPager. Each Fragment gets injected with a realm. Many interactions in a given Question Fragment write data to the DB (some async, some blocking). These Fragments also query the database on onResume to get their updated Data. Some of this data is also copied out of Realm via realm.copyFromRealm(). Now at any given time of these happening, an upload job is most likely running and reading data from the DB and uploading it to a server. When an upload job finishes, it then writes to the DB.
I think I can have up to 7-12 fragment/activities holding a realm reference on the UI thread at a given moment. And 0-6 other references on 0-3 other threads (Background Jobs).
Moreover, I compact my realm DB via Realm.compactRealm(realmConfiguration) on every app launch (perhaps as a separate problem, this doesn't seem to do it's job consistently).
Above I've tried to describe my Realm usage descriptively without going into details. Now my problem is, when a user excessively uses the app (going back and forth between activities/fragments (realm injection + DB read query), uploading data (realm injection + DB read&write query)), I get the above posted Out of Memory Error.
I am also using Leak Canary, and it hasn't detected any leaks. (Not sure if it can anyway)
Am I using Realm in a way it's not supposed to be used? Should I close Realm instances onPause instead of onDestroy? Should I have only one realm instance in an activity and have all it's fragmetns (up to 5 in my case) use this instance? What kind of changes can I make in my app, and perhaps my app architecture to solve this problem?
I appreciate any help in trying to solve this problem.
EDIT: I'm sharing the realm open-close logic in my background threads.
All my jobs share the same realm usage, which is the following:
Realm is injected lazily via:
#Inject protected transient Lazy<Realm> lazyRealm;
The realm object reference is held at the private transient Realm realm; field. I am using Android Priority Job Queue. When the job is added:
#Override
public void onAdded() {
realm = lazyRealm.get();
realm.executeTransaction(realm1 -> {
//write some stuff into realm
});
realm.close();
}
And when the job is run realm is retreived once, and every possible ending of this method has a call to 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 do close their realm instances properly as far as I'm concerned.
While it was true that all my background tasks did call realm.close(), one of them called it too late in it's lifecycle. That was my GPSService, which is a background service. The problem was that GPS service is initialized at the launch of the App as an Android Service, which is rarely destroyed. I was injecting a realm instance onCreate and closing it onDestroy. After the comments of #EpicPandaForce and reading his articles about using realm properly. I realized that this was the cause of the leak. A non-looper thread was keeping an open realm reference for an extremely long time, thus, the mmap was bloating every time a write transaction occures. Now that I moved the realm get/close to happen every time the service runs, my problem is fixed.
My take away is that one needs to treat background thread realm access very delicately. Thank you both for your quick responses and help!
Do you know how to prevent exception when using
Synchronization synchronization = query.findAllSorted("endDate", Sort.DESCENDING).first();
if database is empty?
I'm trying to get the last synchronization in Synchronization table. But if there isn't any record in table, I'm catching an exception.
What's the best way to do that ?
Thank you.
You can do the following:
RealmResults<Synchronization> results = query.findAllSorted("endDate", Sort.DESCENDING);
Synchronization obj = (result.size() > 0) ? result.first() : null;
Although I do agree that it could be a bit easier.
I am trying to delete the last Object from the Realm.io database based on a query, like so:
Realm realm = Realm.getInstance(this);
final RealmResults<RealmCustomLocation> databaseLocations = realm.where(RealmCustomLocation.class).findAllSorted("timeStamp", RealmResults.SORT_ORDER_DESCENDING);
if(databaseLocations.size() >= 4){
realm.beginTransaction();
databaseLocations.removeLast();
realm.commitTransaction();
}
This is exactly like what is written at the Realm.io instructions about deletion:
realm.beginTransaction();
result.removeLast();
realm.commitTransaction()
But when I execute the code it always breaks with a RealmException
io.realm.exceptions.RealmException: Removing object is not supported.
Then I looked at the source code of RealmResults.java and I find this:
So no wonder it keeps crashing, removeLast() does nothing, only throw an error!
So my question is: How can I remove an object from the database then?!
I am using realm.io 0.77 (compile 'io.realm:realm-android:0.77.0') on Android.
I appreciate your help on this!
I have contacted Realm.io support, awaiting an answer. For the meantime:
RealmCustomLocation location = databaseLocations.get(databaseLocations.size() - 1);
location.removeFromRealm();
works equivalent to
databaseLocations.removeLast()
so it can be used as a workaround.
Edit: Support told me that they are fixing it for future versions and recommended to use the workaround I posted for in the mean time.
If you want to delete all objects, then I would create a while loop like this:
while (location.size() > 0) {
location.removeLast();
}