Speicher sparen, da java.lang.OutOfMemoryError



  • Hallo,

    ich versuche grad Dateien einzulesen und von diesen dann den MD5-Hash zu berechnen. Aber nach ca. der 100ten Datei (je ca. 3MB) gibts ein java.lang.OutOfMemoryError: Java heap space.

    Die entsprechenden Stellen:

    private String createMD5FromFile(File file)
      {
        try
        {
          FileInputStream fis=new FileInputStream(file);
          StringBuffer content=new StringBuffer(fis.available());
          byte[] tmp=new byte[2048];
          int l;
    
          while((l=fis.read(tmp))>-1)
            content.append(new String(tmp, 0, l));
    
          fis.close();
    
          return getMD5(content.toString()); //<----Hier wird beim toString abgebrochen
        }
        catch(Exception e)
        {
          return /* ... */;
        }
      }
    
    /* ... */
    
      private String getMD5(String s)
      {
        try
        {
          MessageDigest digest=MessageDigest.getInstance("MD5");
          digest.update(s.getBytes());
          StringBuffer md5=new StringBuffer(32);
          byte[] hash=digest.digest();
    
          for(int i=0; i<hash.length; ++i)
            md5.append(toHex(hash[i]));
    
          return md5.toString();
        }
        catch(Exception e)
        {
          return /* ... */;
        }
      }
    

    Irgendwie stehe ich grad auf dem Schlauch, warum der Speicher nicht wieder freigegeben wird, wenn eine Datei eingelesen wurde. Kann mir jemand einen Tipp geben?

    Grüßle
    Jan



  • Benutze einen Profiler um den Inhalt des Heaps zu analysieren, z.B. jvisualvm (enthalten in JDK 1.6).



  • probier mal damit. sind z.b. wesentlich weniger 'news' drin, als bei dir

    private String createMD5FromFile(File file) throws NoSuchAlgorithmException, IOException
        {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            InputStream is = new FileInputStream(file);
            byte[] buffer = new byte[8192];
            int read;
            String res;
            try
            {
                while ((read = is.read(buffer)) > 0)
                {
                    digest.update(buffer, 0, read);
                }
                res = new BigInteger(1, digest.digest()).toString(16);
            }
            finally
            {
                is.close();
            }
    
            return res;
        }
    


  • An der Anzahl der 'news' kann man wohl kaum Speicherlecks erkennen. Besonders, wenn man nicht das ganze Programm sondern nur 2 Methoden kennt.



  • tfa schrieb:

    An der Anzahl der 'news' kann man wohl kaum Speicherlecks erkennen. Besonders....

    da hast du natürlich recht, obwohl 0 news den heap nicht belasten und unendlich viele in einer funktion zum sofortigen 'out-of-memory' führen würden.

    aber der OP hat in seinem code folgende, fiese konstruktion:

    FileInputStream fis ...;
    StringBuffer content=new StringBuffer(fis.available());
    

    ... und wenn er als input ein mpeg aus seiner pornofilmsammlung angibt, baut er sich 'nen mehrere gigabyte grossen stringbuffer. das ist nicht gut.
    🙂



  • Danke, die Version von fricky funktioniert einwandfrei 🙂 🙂

    Aber jetzt zum Verständnis für mich: Auch wenn meine Version nicht so genial ist, würde mich interessieren, warum dort nach einigen Dateien der Speicher voll ist, und die ganzen ungebrauchten StringBuffers usw. nicht gelöscht werden. Eine Datei, die ich einlesen, ist nicht größer als max. 10MB.



  • Wenn Objekte nicht mehr referenziert werden, werden sie auch gelöscht - früher oder später. Du hast wahrscheinlich ein Speicherleck. Möglicherweise bekommst du wieder OutOfMemoryError, wenn das Programm nur lange genug läuft.



  • Du startest wahrscheinlich ohne Parameter und die VM hat default max 64MB eingestellt, glaub ich. Wenn du jetzt eine Datei mit 10MB Zeichen ein einen StringBuffer einliest und dann noch toString machst und Java Strings 16Bit pro zeichen haben, dann sind das schon mal ca. 40MB. Java braucht ja beim Start sowieso schon einige MB Speicher und so kannst du schon über die 64MB kommen.



  • @tfa: Ich hab mal den Speicherverbrauch beider Versionen im Taskmanager beobachtet, bei meiner Version ging der Speicherverbrauch ruck zuck nach oben, bei der Version von fricky überhaupt nicht.

    Ja, ich starte die VM ohne Parameter, vlt. hast Du recht. Ich probiers vlt. nacher mal aus.

    Danke für die Hilfe! 🙂



  • LeGaN schrieb:

    @tfa: Ich hab mal den Speicherverbrauch beider Versionen im Taskmanager beobachtet, bei meiner Version ging der Speicherverbrauch ruck zuck nach oben, bei der Version von fricky überhaupt nicht.

    Weil du auch erst die ganze Datei in den Speicher liest, bevor du anfängst du rechnen. In der zweiten Version passiert das gleichzeitig, es muss nicht alles im Speicher sein.



  • Ahhh, jetzt ist alles klar 🙂 🙂
    Vielen Dank!


Anmelden zum Antworten