views:

366

answers:

2

The app I am working on needs to read a JSON file that may be anywhere from 1.5 to 3 MB in size. It seems to have no problem opening the file and converting the data to a string, but when it attempts to convert the string to a JSONArray, OutOfMemoryErrors are thrown. The exceptions look something like this:

E/dalvikvm-heap( 5307): Out of memory on a 280-byte allocation.
W/dalvikvm( 5307): Exception thrown (Ljava/lang/OutOfMemoryError;) while throwing internal exception (Ljava/lang/OutOfMemoryError;)

One strange thing about this is that the crash only occurs every 2nd or 3rd time the app is run, leaving me to believe that the memory consumed by the app is not being garbage collected each time the app closes.

Any insight into how I might get around this issue would be greatly appreciated. I am open to the idea of loading the file in chunks, but I'm not quite sure what the best approach is for such a task.

Thank you

+2  A: 

One strange thing about this is that the crash only occurs every 2nd or 3rd time the app is run, leaving me to believe that the memory consumed by the app is not being garbage collected each time the app closes.

That is certainly possible, and if it is the case then it probably due to a memory leak that can be traced back to something that your application is doing. I think you should focus your initial efforts into investigating this aspect ... rather than loading the file in chunks. (I am not familiar with the Android tool-chain, but I am sure it includes memory usage profilers or memory dump analysers.)

EDIT

In response to your followup comment, the fact that it works 2 times in 3 suggests that your app ought to work roughly as-is. Admittedly, you don't have much leeway if the input file gets bigger.

A couple of ideas though:

  • Rather than reading the file into a String and running the JSON parser on the String, use a parser that can read directly from a stream. Your current solution needs space for two complete copies of the data in memory while you are doing the parsing.

  • If the file gets much bigger, you may need to think of a design that doesn't create a complete in-memory representation of the data.

I'm not sure that it is a good idea to read a JSON file in "chunks". This could present problems for parsing the JSON ... depending on exactly what you mean by reading in chunks.

EDIT 2

Maybe what you need is a "SAX like" JSON parser; e.g. http://code.google.com/p/async-json-library/

Stephen C
Are you suggesting that it should be feasible to load this much data then? I have been working on this app for on and off for three months and have yet to see any memory issues at all. I can literally comment out the line of code that converts the string to a json array and everything runs smoothly again.
jeremynealbrown
Thanks for your ideas! I agree that trying to read JSON in chunks seems like trouble. You make a great point about not creating multiple copies of the data too.
jeremynealbrown
I am looking into creating a JSON object from a stream, will check out the async-json-library if the stream approach doesn't work out. Thanks again!
jeremynealbrown
Update: I am not 100% sold on this solution yet ( more debugging needs to be done ), but I replaced the built in org.json classes with com.google.gson and the memory errors are gone. I think this is partially due to the fact that the JsonParser from the gson libs can build objects and arrays from a Reader object, which means the creation of a String representation of the data is unnecessary.
jeremynealbrown
+3  A: 

When you say "2nd or 3rd its run" do you mean each time your starting with a fresh emulator? or do you mean leaving the application and coming back? (for instance pressing home, or calling finalize() )

If your referring to leaving the application and re launching it:

if you haven't set android:launchMode in your manifest to define the activity as singleInstance or singleTask then each time the application is launched a new activity is created and added to the activity stack. You could easily have multiple copies of your activity running in your application process eating a lot of memory.

If its happening the 2nd launch your still using a a lot memory and should break it down more.

Mark
Good point, but launchMode is set to singleTask.
jeremynealbrown