realm / realm-java

Realm is a mobile database: a replacement for SQLite & ORMs
http://realm.io
Apache License 2.0
11.46k stars 1.75k forks source link

After update from 0.77.0 to 0.80.0 started OutOfMemory #1001

Closed rimidalv closed 9 years ago

rimidalv commented 9 years ago

The first: io.realm.internal.OutOfMemoryError: std::bad_alloc in io_realm_internal_tableview.cpp line 481 at io.realm.internal.TableView.nativeClear(Native Method) at io.realm.internal.TableView.clear(Unknown Source) at io.realm.RealmResults.clear(Unknown Source)

The second: io.realm.internal.OutOfMemoryError: std::bad_alloc in io_realm_internal_table.cpp line 302 at io.realm.internal.Table.nativeMoveLastOver(Native Method) at io.realm.internal.Table.moveLastOver(Unknown Source) at io.realm.RealmObject.removeFromRealm(Unknown Source)

Code wasn't changed.

emanuelez commented 9 years ago

Hello, the errors state that you are running out of native memory. It doesn't have much to do wit the new version, apart maybe some new core feature that use more memory than the one shipped with 0.77. My guess is that you're not closing your Realm instances properly.

Can you share more details about your code?

rimidalv commented 9 years ago

for example:
private void deleteFile(AudioItem item, int position) {

    File file = item.getFile();
    if (clickPlayCallback != null && mCurrentItemPos == position)
        clickPlayCallback.onDelete(file);

    Activity activity = mActivity;
    if(activity != null) {
        final Realm realm = DBHelper.getRealm(activity);
        realm.beginTransaction();
        Record record = realm.where(Record.class).equalTo("name", file.getName()).findFirst();
        if (record != null) {
            record.removeFromRealm();
        }
        realm.commitTransaction();
        realm.close();
    }
    file.delete();
    reinitData();
}
emanuelez commented 9 years ago

What does DBHelper do?

rimidalv commented 9 years ago
    public static Realm getRealm(Context context) {        
    String currentRecordPath = StorageSettings.getInstance(context).getCurrentRecordPath();
    String fileName = "database.realm";
    File writeableFolder = new File(currentRecordPath);
    if(!writeableFolder.exists()){
        writeableFolder.mkdirs();
    }
    Realm realm = Realm.getInstance(writeableFolder, fileName);
    return realm;
    }
rimidalv commented 9 years ago

And folder is exist. I checked it

rimidalv commented 9 years ago

I changed code to this:

final Realm realm = DBHelper.getRealm(activity); realm.beginTransaction(); RealmQuery where = realm.where(Record.class); RealmQuery name = where.equalTo("name", file.getName()); Record record = name.findFirst(); L.d("record: "+record); // if (record != null) { // record.removeFromRealm(); // } realm.commitTransaction(); realm.close();

Run in debug and found that real crash in line Record record = name.findFirst();

emanuelez commented 9 years ago

Is that the only place you use Realm in your app? Some explaining is in order. When you use Realm in several threads and one of them is only performing read operations without ever calling refresh, then the core need to retain all the versions of our Realm between the oldest instance and the newest. The biggest this difference grown, the bigger the memory usage. That's why using refresh() when possible is a very good strategy.

rimidalv commented 9 years ago

I don't understand. I read in doc: When you commit a write transaction to a Realm, all other instances of that Realm will be notified, and the read implicit transactions will refresh your Realm objects automatically.

Where i need to do it? And crash is always behavior for this record. And good work for new Record.

rimidalv commented 9 years ago

And i'm return to version 0.77.0 and this code work great. Bug in version 0.80.0 or 78 or 79

emanuelez commented 9 years ago

Are you able to share your code with us, even in a private way? It's hard to figure things out without a reproducible example

rimidalv commented 9 years ago

I'm sorry, but I can't. But i can show my file.getName(), maybe problem in the name? My russian user's complained the same problem. "13 марта 2015 г. 14_05_13.wav"

rimidalv commented 9 years ago

And you can try it if downloaded the app: https://play.google.com/store/apps/details?id=com.rimidalv.dictaphone and use delete recording feature. This problem not on an all recording, only on an old. And if recording begin crash on delete feature - it will be always for this recording.

emanuelez commented 9 years ago

If you cannot share your code, can you maybe create a simple test app that exposes the problem?

rimidalv commented 9 years ago

I'll try to do it tomorrow.

cmelchior commented 9 years ago

Hi @rimidalv Have you been able to reproduce this?

rimidalv commented 9 years ago

I haven't reproduce this on my device, but when i set 0.80.1 - I got this bug again from users with device: LG G3 (g3) Android 4.4 Nexus 4 (mako) Android 5.0 Galaxy S3 (m3) Android 4.4 Nexus 5 (hammerhead) Android 5.1 Moto E (condor_umtsds) Android 4.4

and this stacktrace: io.realm.internal.OutOfMemoryError: std::bad_alloc in io_realm_internal_tableview.cpp line 481 at io.realm.internal.TableView.nativeClear(Native Method) at io.realm.internal.TableView.clear(Unknown Source) at io.realm.RealmResults.clear(Unknown Source) at com.rimidalv.dictaphone.MainActivity.m(Unknown Source) at com.rimidalv.dictaphone.MainActivity.onCreate(Unknown Source)

And I haven't this crash on version 0.77.0

cmelchior commented 9 years ago

Hi @rimidalv Without either a reproducible case or some code to look at, we cannot much more than speculate. That said, OOM errors usually occur in Realm if you are keeping instances of Realm open on background threads without closing them properly. That can result in Realm leaking memory which can lead to OOM.

One way of checking this would eg. be by letting your DPHelper implement a close() method and a counter. That way you can keep track of how many times a Realm has been opened and closed. If that number isn't stable but keep increasing when using the app, it is probably the reason.

Another possibility is that you are iterating over large result sets on each frame redraw eg. a animation. That will result in large amounts of native memory being allocated that cannot be released fast enough due to the Java garbage collector being too slow.

bmunkholm commented 9 years ago

To be more precise, Realm itself is never "leaking" memory (if it does it's a bug), but Realm will keep a snapshot of you the state of your objects from the background thread. Thing about it the same way as keeping a reference to old objects that are then never GC'ed.

cmelchior commented 9 years ago

Closing as I assume the above answered your question.