DataInputStream OutOfMemoryError

Diskussion i 'Frågor, support och diskussion' startad av marka971, 8 jun 2010.

  1. marka971

    marka971 Youth Droid Medlem

    Blev medlem:
    4 sept 2009
    Inlägg:
    178
    Mottagna gillanden:
    6

    MINA ENHETER

    Hej,

    Jag får ibland ett OutOfMemoryError på dis.readLine() i koden nedan som laddar ner lite JSON, som mest ca 41kB.

    Kod:
    InputStream is = null;
    		BufferedInputStream bis = null;
    		DataInputStream dis = null;
    		try {
    			URL url = new URL(address);
    			URLConnection ucon = url.openConnection();
    			is = ucon.getInputStream();
    			bis = new BufferedInputStream(is);
    			dis = new DataInputStream(bis);
    			return dis.readLine();
    		} catch (Exception e) {...
    Här är stacktrace:
    Kod:
    java.lang.OutOfMemoryError
           at java.lang.AbstractStringBuilder.enlargeBuffer(AbstractStringBuilder.java:99)
           at java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:153)
           at java.lang.StringBuffer.append(StringBuffer.java:128)
           at java.io.DataInputStream.readLine(DataInputStream.java:346)
           at se.markus.bfbc2.api.APIManager.downloadJSON(APIManager.java:88)...
    Nån som har ett tips på hur jag undviker felet?

    /Markus
     
  2. sandos

    sandos Adult Droid Medlem

    Blev medlem:
    27 maj 2009
    Inlägg:
    715
    Mottagna gillanden:
    8

    MINA ENHETER

    Är du säker på att det är så lite data även när du får OOM? Använder appen mycket data i övrigt? Jag tror android-appar oftast har en gräns på 16MB (beror på ROMen, vet inte om det brukar tweakas dock) så det borde räcka gott och väl.
     
  3. pruzze

    pruzze Youth Droid Medlem

    Blev medlem:
    11 aug 2009
    Inlägg:
    145
    Mottagna gillanden:
    10

    MINA ENHETER

    Kan det inte vara en bra idé att stänga strömmarna? Visserligen ska de väl stängas av garbage-collectorn, men den kanske inte hänger mer?
     
  4. sandos

    sandos Adult Droid Medlem

    Blev medlem:
    27 maj 2009
    Inlägg:
    715
    Mottagna gillanden:
    8

    MINA ENHETER

    Jag antog att det fanns en finally { längre ner, men ja det är nog en mycket god ide att stänga de! :)
     
  5. marka971

    marka971 Youth Droid Medlem

    Blev medlem:
    4 sept 2009
    Inlägg:
    178
    Mottagna gillanden:
    6

    MINA ENHETER

    Hej,

    Det finns en finally-sats längre ner som stänger strömmarna, så det har nog inte med det att göra...

    Så här ser hela metoden ut:
    Kod:
    	private static String downloadJSON(String address) throws APIException {
    		InputStream is = null;
    		BufferedInputStream bis = null;
    		DataInputStream dis = null;
    		try {
    			URL url = new URL(address);
    			URLConnection ucon = url.openConnection();
    			is = ucon.getInputStream();
    			bis = new BufferedInputStream(is);
    			dis = new DataInputStream(bis);
    			return dis.readLine();
    		} catch (Exception e) {
    			Log.d(TAG, "Error: " + e);
    			throw new APIException("Error downloading data", e);
    		} finally {
    			if (dis != null) {
    				try {
    					dis.close();
    				} catch (IOException e) {
    					Log.e(TAG, "Error closing datainputstream", e);
    				}
    			}
    
    			if (bis != null) {
    				try {
    					bis.close();
    				} catch (IOException e) {
    					Log.e(TAG, "Error closing BufferedInputStream", e);
    				}
    			}
    
    			if (is != null) {
    				try {
    					is.close();
    				} catch (IOException e) {
    					Log.e(TAG, "Error closing inputstream", e);
    				}
    			}
    		}
    	}
    
    /Markus
     
    Last edited: 9 jun 2010
  6. sandos

    sandos Adult Droid Medlem

    Blev medlem:
    27 maj 2009
    Inlägg:
    715
    Mottagna gillanden:
    8

    MINA ENHETER

    Som sagt, det låter väldigt mysko. Antingen är du på gränsen att få slut på minne hela tiden, eller så har du läst en stor rad :)

    Jag skulle catchat Throwable (inkluderar OOM) och satt en breakpoint där, och försökt se vad som händer? Men man kan väl förstås inte se något i android-sourcen :(
     
  7. sandos

    sandos Adult Droid Medlem

    Blev medlem:
    27 maj 2009
    Inlägg:
    715
    Mottagna gillanden:
    8

    MINA ENHETER

    Jag blev lite nyfiken på hur mkt 40k tecken i en stringbuffer kunde dra så testade denna:

    Kod:
            Log.v("majs", "Free: " + Runtime.getRuntime().freeMemory());
            
            StringBuffer sb = new StringBuffer();
            for(int i=0; i<40000; i++) {
            	sb.append("s");
            }
            
            Log.v("majs", "Free: " + Runtime.getRuntime().freeMemory());
    Och det drar iofs en del:

    Men som sagt, 150k bytes borde inte vara problem att fixa.
     
  8. dinamic

    dinamic Baby Droid Medlem

    Blev medlem:
    1 jun 2010
    Inlägg:
    25
    Mottagna gillanden:
    0

    MINA ENHETER

    Är OOM sporadiska eller kommer dem efter en tid?

    Kan det vara så att de 150k stringarna är direct allokerade och inte frigörs utav GC ?

    Kolla din apps minnesutnyttjande och se om det växer med tiden !?
     
  9. marka971

    marka971 Youth Droid Medlem

    Blev medlem:
    4 sept 2009
    Inlägg:
    178
    Mottagna gillanden:
    6

    MINA ENHETER

    Hej,

    Problemet är att jag har aldrig fått detta fel själv, utan endast via ett antal felrapporter. Jag har ca 1600 installationer ute, gånger X antal widgets per applikation som uppdateras en gång i halvtimmen. Jag får ett par felrapporter om dagen, så det är inte särskilt ofta det händer...

    /Markus
     
  10. dinamic

    dinamic Baby Droid Medlem

    Blev medlem:
    1 jun 2010
    Inlägg:
    25
    Mottagna gillanden:
    0

    MINA ENHETER

    Även om du inte får felet kan du se om minnet växer under period på din hårdvara.. sen kan det förståss vara olika gränser mellan version etc som nämnts tidigare..
    En direct buffer är native allokerat minne utanför jvm så jag et inte om en taskmanager skulle visa rätt, jag skulle köra adb shell och sen top för att monitorera processen...

    Du kan kolla om en buffer är direct med Buffer.isDirect()
     
  11. the_bean

    the_bean Youth Droid Medlem

    Blev medlem:
    20 apr 2010
    Inlägg:
    134
    Mottagna gillanden:
    2

    MINA ENHETER

  12. marka971

    marka971 Youth Droid Medlem

    Blev medlem:
    4 sept 2009
    Inlägg:
    178
    Mottagna gillanden:
    6

    MINA ENHETER

  13. Kaj

    Kaj Senior Droid Medlem

    Blev medlem:
    12 jun 2009
    Inlägg:
    1 768
    Mottagna gillanden:
    44

    MINA ENHETER

    Bara en liten kommentar. När du stänger den "yttre" strömmen så anropar den close på den aggregerade strömmen. Dvs, du behöver inte anropa close på alla, bara på den yttre.
     
  14. sandos

    sandos Adult Droid Medlem

    Blev medlem:
    27 maj 2009
    Inlägg:
    715
    Mottagna gillanden:
    8

    MINA ENHETER

    Jag skulle nog ha infört denna loggning:

    Spara undan heapsize + free memory innan denna kodsnutt, lägg in en catch Throwable, om du fångar OutOfMemoryException så logga de gamla värdena samt nya värden.

    Kanske inte ger så mycket, men då kan man iaf se om minnesanvändningen har gått upp omotiverat mycket precis här eller om den var hög redan innan.

    Sedan får du be folk skicka log. :)
     
  15. PatrikS

    PatrikS Senior Droid Medlem

    Blev medlem:
    29 jun 2009
    Inlägg:
    1 123
    Mottagna gillanden:
    65

    MINA ENHETER

    En del av svaret finns iaf i abstractbuilder... den försöker förstora buffern och och med det är det godnatt.

    Frågan är bara hur stor buffer den vill ha.
    Jag är tveksam till om DataInputStream är rätt klass att använda ö.h taget..

    kanske :
    Kod:
     BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder sb = new StringBuilder();
     
            String line = null;
            try {
                while ((line = reader.readLine()) != null) {
                    sb.append(line + "\n");
                }
    
    eller

    Kod:
     BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder sb = new StringBuilder();
            int c;
    
            try {
                while ((c = reader.read()) != -1) {
                    sb.append((char)reader.read());
                }
    
               return sb.toString();
    
    och stänga osv i sedvanlig ordning....
    Frågan man kan ställa är om dina klienter har flera versioner av widget:en liggande på flera platser och det blir strul med resurser som inte släpps fria.. eller trådar som blir svåra att GC.
     
    Last edited: 10 jun 2010