9 Replies Latest reply: Jul 22, 2013 6:45 PM by jtahlborn RSS

    Does OutputStream.write() has a memory leak on Linux?

    tttzhang2000

      I write a piece of java code to create 500K small files (average 40K each) on CentOS. The original code is like this:

       

           package MyTest;

           import java.io.*;

           public class SimpleWriter {

      public static void main(String[] args) {
        String dir = args[0];
        int fileCount = Integer.parseInt(args[1]);

        String content="@#$% SDBSDGSDF ASGSDFFSAGDHFSDSAWE^@$^HNFSGQW%#@&$%^J#%@#^$#UHRGSDSDNDFE$T#@$UERDFASGWQR!@%!@^$#@YEGEQW%!@%!!GSDHWET!^";
        StringBuilder sb = new StringBuilder();
        int count = 40 * 1024 / content.length();
        int remainder = (40 * 1024) % content.length();
        for (int i=0; i < count; i++)
        {
         sb.append(content);
        }
        if (remainder > 0)
        {
         sb.append(content.substring(0, remainder));
        }
       
        byte[] buf = sb.toString().getBytes();
       
        for (int j=0; j < fileCount; j++)
        {
         String path = String.format("%s%sTestFile_%d.txt", dir, File.separator, j);
         try{
          BufferedOutputStream fs = new BufferedOutputStream(new FileOutputStream(path));
          fs.write(buf);
          fs.close();
         }
         catch(FileNotFoundException fe)
         {
          System.out.printf("Hit filenot found exception %s", fe.getMessage());
         }
         catch(IOException ie)
         {
          System.out.printf("Hit IO exception %s", ie.getMessage());
        
         }

        }
      }

            }

      You can run this by issue following command:
        java -jar SimpleWriter.jar my_test_dir 500000

      I thought this is a simple code, but then I realize that this code is using up to 14G of memory.  I know that because when I use free -m to check the memory, the free memory kept dropping, until my 15G memory VM only had 70 MB free memory left.  I compiled this using Eclipse, and I compile this against JDK 1.6 and then JDK1.7. The result is  the same.  The funny thing is that, if I comment out fs.write(), just open and close the stream, the memory stabilized at certain point.  Once I put fs.write() back, the memory allocation just go wild.  500K 40KB files is about 20G.  It seems Java's stream writer never deallocate its buffer during the operation.

      I once thought java GC does not have time to clean.  But this make no sense since I closed the file stream for every file.  I even transfer my code into C#, and running under windows, the same code producing 500K 40KB files with memory stable at certain point, not taking 14G as under CentOS.  At least C#'s behavior is what I expected, but I could not believe Java perform this way.  I asked my colleague who were experienced in java.  They could not see anything wrong in code, but could not explain why this happened.  And they admit nobody had tried to create 500K file in a loop without stop.

      I also searched online and everybody says that the only thing need to pay attention to, is close the stream, which I did.

      Can anyone help me to figure out what's wrong?

      Can anybody also try this and tell me what you see?

       

      BTW, some people in online community tried the code on Windows and it seemed to worked fine.  I didn't tried it on windows.  I only tried in Linux as I thought that where people use Java for.  So, it seems this issue happened on Linux).

      I also did the following to limit the JVM heap, but it take no effects
          java -Xmx2048m -jar SimpleWriter.jar my_test_dir 500000

        • 1. Re: Does OutputStream.write() has a memory leak on Linux?
          sabre150

          I don't see the problem using the latest xubuntu with Oracle 32 bit JDK 1.7.0_25  but  I only have 4 G memory.

           

          "free -m" during the run produces -

           

                       total       used       free     shared    buffers     cached

          Mem:          4033       3809        223          0        137       2676

          -/+ buffers/cache:        995       3038

          Swap:         4092          0       4092

          sabre@beta:~/temp$ free -m

                       total       used       free     shared    buffers     cached

          Mem:          4033       3781        252          0        148       2629

          -/+ buffers/cache:       1003       3029

          Swap:         4092          0       4092

          sabre@beta:~/temp$ free -m

                       total       used       free     shared    buffers     cached

          Mem:          4033       3785        248          0        162       2632

          -/+ buffers/cache:        990       3043

          Swap:         4092          0       4092

          sabre@beta:~/temp$ free -m

                       total       used       free     shared    buffers     cached

          Mem:          4033       3758        274          0        173       2624

          -/+ buffers/cache:        960       3072

          Swap:         4092          0       4092

           

          I'm always skeptical of reported JRE memory leaks but I don't rule it out. You should note that the JVM is not obliged to run the GC if there is enough memory available for it's needs. It only needs to run the GC before it gives up the ghost with an OOM exception.

           

          P.S. You will find that most people here use Windows and not Linux.

          • 2. Re: Does OutputStream.write() has a memory leak on Linux?
            Alex Geller

            >You should note that the JVM is not obliged to run the GC if there is enough memory available for it's needs.

            Exactly, I tested on Linux with -Xmx5m (Limiting VM memory to 5 MB) and the program ran to completion in 16 minutes so there is no memory leak.

            I had to make a minor change to the program to keep it from exhausting my disk space which probably isn't relevant but I am posting it for completeness. I added "%500" to the following line:

             

            String path = String.format("%s%sTestFile_%d.txt", dir, File.separator, j%500);
            
            • 3. Re: Does OutputStream.write() has a memory leak on Linux?
              jtahlborn

              actually, you missed a key point about using streams in Java, you should always close them in a finally block!  I suspect that when you ran your test you started getting io exceptions, and that left the file streams open (since you are not closing them in a finally block).

               

              Also, if you suspect a memory leak, the best thing to do is generate a heap dump.  you can then examine this to determine the exact cause of the memory leak instead of guessing.

              • 4. Re: Does OutputStream.write() has a memory leak on Linux?
                tttzhang2000

                Would you please let me know which OS you use to run the test?   I ran with Java -Xmx2048m -jar SimpleWriter.jar ...  in my CentOS VM in AWS which has 15G memory and a lot of disk space, I saw with free -m command that memory was eaten up way pass 2G.  The -Xmx basically had no effects.   Did you use free -m to check free memory?

                • 5. Re: Does OutputStream.write() has a memory leak on Linux?
                  jtahlborn

                  When you are talking about "memory", are you talking about actual memory used by the jvm, or the general system memory?  And if you are talking about system memory (which it seems like since you are using "free -m" to measure it), what is the value in the "cached" column?

                  • 6. Re: Does OutputStream.write() has a memory leak on Linux?
                    tttzhang2000

                    a) I print out exception if there's IOException, and when I ran the program there's no error happened what's all ever.

                    b) when I put fs.close into finally block, Eclipse complains that I have to catch IOException in finally block.  Is that kind of defeat the purpose of using finally block?   If I still have catch IOException inside finally block, then, my original code which put fs.close in try block already catch IOException, no need to put it in finally block.

                    • 7. Re: Does OutputStream.write() has a memory leak on Linux?
                      tttzhang2000

                      Good point, I actually run cat /proc/meminfo. And I got the following.  Why I have so big of Inactive(file) allocation?

                      MemTotal:       15239492 kB

                      MemFree:           83424 kB

                      Buffers:           76012 kB

                      Cached:         13920152 kB

                      SwapCached:            0 kB

                      Active:           391104 kB

                      Inactive:       14181268 kB

                      Active(anon):     288124 kB

                      Inactive(anon):   288268 kB

                      Active(file):     102980 kB

                      Inactive(file): 13893000 kB

                      Unevictable:           0 kB

                      Mlocked:               0 kB

                      SwapTotal:        500432 kB

                      SwapFree:         500432 kB

                      Dirty:           2568700 kB

                      Writeback:          6064 kB

                      AnonPages:        576292 kB

                      Mapped:             9884 kB

                      Shmem:               140 kB

                      Slab:             472340 kB

                      SReclaimable:     421692 kB

                      SUnreclaim:        50648 kB

                      KernelStack:        1192 kB

                      PageTables:         4132 kB

                      NFS_Unstable:          0 kB

                      Bounce:                0 kB

                      WritebackTmp:          0 kB

                      CommitLimit:     8120176 kB

                      Committed_AS:     835900 kB

                      VmallocTotal:   34359738367 kB

                      VmallocUsed:       38908 kB

                      VmallocChunk:   34359687700 kB

                      HardwareCorrupted:     0 kB

                      AnonHugePages:         0 kB

                      HugePages_Total:       0

                      HugePages_Free:        0

                      HugePages_Rsvd:        0

                      HugePages_Surp:        0

                      Hugepagesize:       2048 kB

                      DirectMap4k:    15728640 kB

                      DirectMap2M:           0 kB

                       

                      BTW, my test used up all the memory again, and free -m show this

                                   total       used       free     shared    buffers     cached

                      Mem:         14882      14805         76          0         80      13583

                      -/+ buffers/cache:       1142      13740

                      Swap:          488          0        488

                       

                      Any hint what's going on?

                      • 8. Re: Does OutputStream.write() has a memory leak on Linux?
                        jtahlborn

                        Ah, as i suspected.  you really only have 300MB of memory in use.  the rest is just linux caching recently used stuff.  that's how linux is built.  it uses your "free" memory to cache stuff which was recently used.  if you really need more memory, it will just dump the cached stuff.  but, if you re-use something you had recently discarded, it can pull it straight from memory.

                         

                        so, in other words, there is no memory leak.

                        • 9. Re: Does OutputStream.write() has a memory leak on Linux?
                          jtahlborn

                          i realize it's not convenient to put the close call within the finally block, however, that does not make it any less correct.  correct code is not always convenient.  many people have a convenience method which does a close with the extra catch which is useful for closing Closeables in a finally block.  if you are using jdk 7, you can use the try w/ resources feature.