7

Gibt es ein gutes Beispiel, um Datei-Deskriptor-Leck in Android zu demonstrieren? Ich habe irgendwo gelesen, dass es passiert, wenn wir die Streams nicht schließen, zum Beispiel FileInputStream oder FileOutputStream, aber ich konnte kein gutes Referenzbeispiel finden, das es zeigt.Beispiel für ein Dateideskriptor-Leck?

Bitte teilen Sie einige Blog/Code-Snippet. Danke!

+1

Öffnen Sie einfach eine Reihe von Dateien oder Ports ohne zu schließen? Nach ein paar tausend Fenstern hört auf, sie zu geben. Ähnlich in anderen Betriebssystemen. – Ordous

+0

@Ordous, vielen Dank für Ihre schnelle Antwort. Könnten Sie bitte ein funktionierendes Code-Snippet davon posten – VicJordan

Antwort

0
InputStream in; 
try { 
    in = new BufferedInputStream(socket.getInputStream()); 

    // Do your stuff with the input stream 
} catch (Exception e) { 
    // Handle your exception 
} finally { 
    // Close the stream here 
    if (in != null) { 
     try { 
      in.close(); 
     } catch (IOException e) { 
      Log.e(TAG, "Unable to close stream: " + e); 
     } 
    } 
} 

Die Idee ist, Ihre Dateideskriptors im finally Block zu schließen. Wenn Sie erfolgreich abgeschlossen haben oder eine Ausnahme auftritt, wird der Dateideskriptor ordnungsgemäß geschlossen.

Nun, wenn Sie nach etwas suchen zu zeigen, wie NICHT dies richtig zu tun, wickeln Sie einfach diesen Code in einer while(1) Schleife, kommentieren Sie die in.close() Linie und legt ein break; in Ihrem Block fängt, so dass, wenn es bläst Bis Sie aus Ihrer Endlosschleife ausbrechen.

+0

1) Diese Frage heißt "File descriptor leak example", nicht "wie schließe ich Streams richtig". Du machst das nicht einmal so gut.2) * wickle einfach diesen Code in eine Weile (1) Schleife * qualifiziert sich nicht als "Leck" (wie in "zufälliger permanenter Verlust von Ressourcen") 3) Es gibt eine ziemlich gute Chance, dass Stack-Trace-Erstellung verfolgt wird durch einen GC-Pass, der die meisten "ausgelaufenen" Streams schnell finalisiert. – user1643723

+1

Es tut mir leid, ich denke ich habe deine Frage nicht verstanden. Wenn man bedenkt, dass Sie diese Frage einen Monat lang mit einem Kopfgeld beantwortet haben und niemand darauf geantwortet hat, bin ich wahrscheinlich nicht der Einzige, der nicht versteht, wonach Sie fragen. Viel Glück. – Joboodi

-1
InputStream in; 

try { 

    in = new FileInputStream(new File("abc"); 


    in.read(); // Do some stuff with open fileinputstream 
    // If an exception is generated, inputstream object will not be closed 
    // as the next statement will not be executed, instead jumping to 
    // the catch block. this will cause a leak of the fd assigned to file 
    // "abc" while opening it 
    in.close()' 
    } catch (Exception e) { 

    // Handle your exception 

    } 
6

Da Dalvik Fileinputstream wird close itself when it is garbage collected (dies ist für OpenJDK/Oracle auch wahr) ist weniger verbreitet als man denken würde, um tatsächlich Filedeskriptoren auslaufen. Natürlich werden die Dateideskriptoren solange "durchgesickert", bis der GC läuft. Je nach Programm kann es eine Weile dauern, bis sie wiederhergestellt sind.

Um ein dauerhafteres Leck zu erreichen, müssen Sie verhindern, dass der Datenstrom gesammelt wird, indem Sie irgendwo im Speicher einen Verweis darauf führen.

Hier ist ein kurzes Beispiel, das eine Eigenschaftsdatei alle 1 Sekunden geladen und verfolgt jedes Mal, es hat sich geändert:

public class StreamLeak { 

    /** 
    * A revision of the properties. 
    */ 
    public static class Revision { 

     final ZonedDateTime time = ZonedDateTime.now(); 
     final PropertiesFile file; 

     Revision(PropertiesFile file) { 
      this.file = file; 
     } 
    } 

    /* 
    * Container for {@link Properties} that implements lazy loading. 
    */ 
    public static class PropertiesFile { 

     private final InputStream stream; 
     private Properties properties; 

     PropertiesFile(InputStream stream) { 
      this.stream = stream; 
     } 

     Properties getProperties() { 
      if(this.properties == null) { 
       properties = new Properties(); 
       try { 
        properties.load(stream); 
       } catch(IOException e) { 
        e.printStackTrace(); 
       } 
      } 
      return properties; 
     } 

     @Override 
     public boolean equals(Object o) { 
      if(o instanceof PropertiesFile) { 
       return ((PropertiesFile)o).getProperties().equals(getProperties()); 
      } 
      return false; 
     } 
    } 

    public static void main(String[] args) throws IOException, InterruptedException { 
     URL url = new URL(args[0]); 
     LinkedList<Revision> revisions = new LinkedList<>(); 
     // Loop indefinitely 
     while(true) { 
      // Load the file 
      PropertiesFile pf = new PropertiesFile(url.openStream()); 
      // See if the file has changed 
      if(revisions.isEmpty() || !revisions.getLast().file.equals(pf)) { 
       // Store the new revision 
       revisions.add(new Revision(pf)); 
       System.out.println(url.toString() + " has changed, total revisions: " + revisions.size()); 
      } 
      Thread.sleep(1000); 
     } 
    } 
} 

Wegen der trägen Laden halten wir die Input im PropertiesFile die wird beibehalten, wenn wir eine neue Revision erstellen und da wir den Stream nie schließen, werden wir hier Dateideskriptoren verlieren.

nun diese offenen Dateideskriptoren wird durch das OS geschlossen werden, wenn das Programm beendet wird, aber solange das Programm läuft es Filedeskriptoren auslaufen wird auch weiterhin wie lsof unter Verwendung zu sehen:

$ lsof | grep pf.properties | head -n 3 
java 6938 raniz 48r  REG 252,0 0 262694 /tmp/pf.properties 
java 6938 raniz 49r  REG 252,0 0 262694 /tmp/pf.properties 
java 6938 raniz 50r  REG 252,0 0 262694 /tmp/pf.properties 
$ lsof | grep pf.properties | wc -l  
431 

Und wenn wir die GC zu laufen zwingen, können wir sehen, dass die meisten von ihnen zurückgegeben werden:

$ jcmd 6938 GC.run 
6938: 
Command executed successfully 
$ lsof | grep pf.properties | wc -l 
2 

die verbleibenden zwei Deskriptoren sind die in den Revision s gespeichert diejenigen.

Ich lief dies auf meinem Ubuntu-Rechner, aber die Ausgabe würde ähnlich aussehen, wenn auf Android laufen.