javaandroidjvmjvm-crasheuropean-data-format

Android App running into java.lang.OutOfMemoryError while EDF file parsing


I have searched around for simmilar topics but could not find anything related to EDF files and Out of memory error together.

So, here is my problem: I am developing an Android app in Android studio which browses files, selects an '.edf' file and parses it. But for some files it throws the following error (with stacktrace):

I/xample.styleap: Starting a blocking GC Alloc
    Starting a blocking GC Alloc
I/xample.styleap: Alloc concurrent copying GC freed 15804(1107KB) AllocSpace objects, 5(108KB) LOS objects, 86% free, 1955KB/13MB, paused 148us total 44.577ms
    Starting a blocking GC Alloc
I/xample.styleap: Alloc young concurrent copying GC freed 0(15KB) AllocSpace objects, 0(0B) LOS objects, 86% free, 1955KB/13MB, paused 120us total 16.603ms
    Starting a blocking GC Alloc
I/xample.styleap: Alloc concurrent copying GC freed 2019(110KB) AllocSpace objects, 0(0B) LOS objects, 86% free, 1844KB/13MB, paused 83us total 25.049ms
    Forcing collection of SoftReferences for 488MB allocation
    Starting a blocking GC Alloc
I/xample.styleap: Alloc concurrent copying GC freed 2053(69KB) AllocSpace objects, 0(0B) LOS objects, 87% free, 1790KB/13MB, paused 75us total 22.140ms
W/xample.styleap: Throwing OutOfMemoryError "Failed to allocate a 512000012 byte allocation with 12582912 free bytes and 190MB until OOM, target footprint 14416192, growth limit 201326592" (VmSize 1212600 kB)
I/xample.styleap: Starting a blocking GC Alloc
    Starting a blocking GC Alloc
I/xample.styleap: Alloc young concurrent copying GC freed 4(31KB) AllocSpace objects, 0(0B) LOS objects, 87% free, 1774KB/13MB, paused 79us total 15.057ms
I/xample.styleap: Starting a blocking GC Alloc
I/xample.styleap: Alloc concurrent copying GC freed 3(16KB) AllocSpace objects, 0(0B) LOS objects, 87% free, 1774KB/13MB, paused 83us total 23.495ms
    Forcing collection of SoftReferences for 488MB allocation
    Starting a blocking GC Alloc
I/xample.styleap: Alloc concurrent copying GC freed 50(17KB) AllocSpace objects, 0(0B) LOS objects, 87% free, 1788KB/13MB, paused 73us total 22.532ms
W/xample.styleap: Throwing OutOfMemoryError "Failed to allocate a 512000016 byte allocation with 12582912 free bytes and 190MB until OOM, target footprint 14414784, growth limit 201326592" (VmSize 1212600 kB)
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.styleapp, PID: 30041
    java.lang.OutOfMemoryError: Failed to allocate a 512000016 byte allocation with 12582912 free bytes and 190MB until OOM, target footprint 14414784, growth limit 201326592
        at br.unb.biologiaanimal.edf.EDFReader.readRecords(EDFReader.java:106)
        at br.unb.biologiaanimal.edf.EDFReader.<init>(EDFReader.java:25)
        at br.unb.biologiaanimal.edf.EDF.<init>(EDF.java:24)
        at com.example.styleapp.MainActivity.runEDFfile(MainActivity.java:100)
        at com.example.styleapp.MainActivity.access$000(MainActivity.java:24)
        at com.example.styleapp.MainActivity$2.onMenuItemClick(MainActivity.java:82)
        at androidx.appcompat.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:154)
        at androidx.appcompat.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:991)
        at androidx.appcompat.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:981)
        at androidx.appcompat.widget.ActionMenuView.invokeItem(ActionMenuView.java:625)
        at androidx.appcompat.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:151)
        at android.view.View.performClick(View.java:7140)
        at android.view.View.performClickInternal(View.java:7117)
        at android.view.View.access$3500(View.java:801)
        at android.view.View$PerformClick.run(View.java:27355)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7410)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)
W/System: A resource failed to call close.

I am using EDF parser library for java : https://github.com/lab-neuro-comp/EDF

My source code (MainActivity.java):

import br.unb.biologiaanimal.edf.EDF;
EDF edf = new EDF(filepath);

It fails while EDF constructor is called with the specific filepath. At many places, it has been said that it fails due to shortage of VM in JVM/Android or heap size. But i think i have taken care of that too:

Inside studio.vmoptions

-Xmx2048m

I guess this specifies Android Studio's JVM VM size. Am also printing maximum amount of memory that the Java virtual machine will attempt to use:

Runtime rt = Runtime.getRuntime();
long maxMemory = rt.maxMemory();

This prints

201326592

That means 201MB approx is available.

Then what is the reason for it fails (runs out of memory) while loading a 1.9 MB EDF file when it successfuly loads a 4.2MB EDF file?


Solution

  • It was failing because of a logical bug of the EDF parsing library. The EDF class method looks for a header called "duration" (the reason is unclear) and uses it for subsequent arithmetic operations where it use the header value to allocate array memory etc. at times the duration value is float and that leads to wrong arithmetic also the resultant values become significantly large. There it fails to allocate memory. here is the snippet of the code:

    private void readRecords(FileInputStream stream)
        throws IOException
        {
            records = new HashMap();
            int[] numberSamples = getNumberSamples();
            int[] sampling = new int[numberSignals];
            int duration = paramToInt("duration");
            int dataRecords = paramToInt("datarecords");
            byte[][] recordList = new byte[numberSignals][5];
            String[] labels = getLabels();
            byte[] buffer = null;
    
            // Initalizing variables
            for (int i = 0; i < numberSignals; ++i)
            {
                sampling[i] = duration * numberSamples[i];
                recordList[i] = null;
            }
    
            // Reading records
            for (int d = 0; d < dataRecords; ++d)
            {
                for (int i = 0; i < numberSignals; ++i)
                {
                    duration = 2 * sampling[i];
                    buffer = new byte[duration];
                    stream.read(buffer);
                    // TODO Make this insertion faster. Maybe a queue?
                    recordList[i] = (recordList[i] == null)?
                        buffer :
                        EDFUtil.insert(recordList[i], buffer);
                }
            }
    
            // Packing records
            for (int i = 0; i < numberSignals; ++i)
            {
                String label = labels[i].trim();
                records.put(label, recordList[i]);
            }
        }