0

I've got problem with releasing memory by jvm. I know that java release memory of thread resoruces after its exit from run method. And other objects are deleting by garbage collector when they don't have referencees with some exceptions like windows/frames. Why in below code gc doesn't release memory of byte array despite of threads end their work? I know System.gc() is only suggestion for gc but i use it just in case and assigning null for byte array reference is unnecessary.
Below code is only example and i've got real problem in my client-server application in similar case when server send files to clients.

 private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {                                         
   int j=0;
    for (int i=0;i<5;i++){
       try {
           Thread.sleep(2000);
       } catch (InterruptedException ex) {
           Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
       }
       j++;
       new Thread(new Runnable(){
           public void run(){
               byte[] bytes=new byte[1024*1024*100];
               try {
                   Thread.sleep(15000);
               } catch (InterruptedException ex) {
                   Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
               }
               System.out.println("exiting "+Thread.currentThread().getName());
               bytes=null;
               System.gc();
           }
       }, ""+j).start();

   }
System.gc();     
} 

I leave above problem and go to be practical. Earlier there was loading whole file to one byte array and sending it using writeObject(), but it causes memory problems. Look at that code:

                BufferedOutputStream bos = null;                    
                byte[] bytes;
                int count;
                for (int i = 0; i < filesToUpdate.size(); i++) {                      
                    if (mapp.get(filesToUpdate.get(i)) == null) {
                        addNewFile(filesToUpdate.get(i));
                    }                        
                    bos = new BufferedOutputStream(new FileOutputStream(new File(filesToUpdate.get(i))));                        
                    long bufferSize = ois.readLong();                        
                    ous.writeObject(2);
                    ous.flush();                                                                             
                    bytes =new byte[8192];                       
                    while ((count=ois.read(bytes))>0){
                        bos.write(bytes, 0, count);                
                    }
                    bos.flush();
                    bos.close();                                                                  
                    ous.writeObject(3);
                    ous.flush();                        
                }
                ois.readObject();                    
                updateRevision(mapp, filesToUpdate);

It's client side which receives files. READ METHOD BLOCKS in first file after it received last packet. And here is server side:

       int count;           
        File file;
        FileInputStream fis=null;
        byte[] bytes;
        for (int i=0;i<filesForPatch.size();i++){
            if (pc.getWhat()==0)
                path="admin/";
            else path="client/";
            path+=filesForPatch.get(i);
            file=new File(path);                           
            long buffSize=file.length();
            ous.writeLong(buffSize);
            ous.flush();                                             
            ois.readObject();               
            fis=new FileInputStream(file);
            bytes=new byte[8192];
            while ((count=fis.read(bytes))>0){
                ous.write(bytes, 0, count);
            }
            ous.flush();
            fis.close();
            ois.readObject();             
        }    

Any ideas how to solve this problem?

  • How did you check that Java is not releasing the memory of the byte array? Basically, can you post something to support your claim?? – Chetan Kinger Mar 29 '15 at 10:54
  • I check it by windows task manager. – Maciej Dzierżak Mar 29 '15 at 10:57
  • Can you elaborate on what stats of the windows task manager got you to believe that it is the byte array that is causing the memory leak and not some other part of your program, or a different application on your system all-together? – Chetan Kinger Mar 29 '15 at 10:59
  • It's very simple program there is only above code and frame with button creating. So what else can do that memory leak? – Maciej Dzierżak Mar 29 '15 at 11:05
  • That's right. So windows task manager is not telling you it's the byte array. You feel that it's the byte array that's the culprit right? Why do you feel the byte array is the culprit? – Chetan Kinger Mar 29 '15 at 11:05
  • As you can see i allocate five threads with 100 mb byte array for every thread. WTM show a bit over 500 mb ram for that program after creating every byte array. Before creating those byte arrays program gets only 40 mb of ram. And there aren't any other elements getting a lot of memory. – Maciej Dzierżak Mar 29 '15 at 11:11
  • You may want to have a look at http://stackoverflow.com/questions/11919400/release-java-memory-for-os-at-runtime – halfbit Mar 29 '15 at 11:14
  • How long did you monitor the memory in the windows task manager. I believe there is no reason why the byte array should not be collected by the garbage collector. If the memory used by your system remains at 500 MB for more than 30 - 60 minutes, that's when I would go investigate with a profiler. As the program stands, I don't see any reason why the byte array won't be collected. – Chetan Kinger Mar 29 '15 at 11:16

2 Answers2

1

The info you get from the Windows Task Manager doesn't mean a lot. The JVM dynamically sizes the heap according to many factors, throughput being the first concern. The heap size is never exactly equal to the actual memory allocated by reachable objects.

If you want to observe the effects of garbage collection, then connect to your JVM with VisualVM. For best effects install the VisualGC plugin and open its tab, there you will be able to observe the changes in the size of all generations in real time. Garbage collections will be immediately reflected in the shown occupancy and you'll also be able to notice when the heap itself is being resized (rarely).

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • How i solve this problem in my server-client application. I load file to byte array and send it to client. During every file sending jvm allocate ram at size about file size and doesn't release its. Server works in infinite loop and after some clients download files memory for server is about few gigabytes and application throws no more available memory exception. – Maciej Dzierżak Mar 29 '15 at 12:10
  • That description sounds like a memory leak, it is not an issue with the garbage collector. You have to see why the arrays are staying reachable all the time. But BTW copying the whole file to memory is not the optimal approach, you should *stream* the file directly from disk to network, then from network to file at the server side. Only a buffer's worth of data should be in RAM at any point in time. – Marko Topolnik Mar 29 '15 at 13:18
  • I firstly do like you said but i have problem with blocking read of input stream after stream receives last packet. Available() method doesn't solve the problem because it gets data from next file i send and saves it to previous file. – Maciej Dzierżak Mar 29 '15 at 14:07
  • Any problem you may have had is due to incorrect API usage, like not properly closing the output stream at the sending side. – Marko Topolnik Mar 29 '15 at 18:01
  • Look at my first post (question). I paste my code containing problem with stream. – Maciej Dzierżak Mar 29 '15 at 20:06
0

for (int i=0;i<5;i++){

This most likely is an insufficient amount of time for the GC to settle into a stable state for heap size. If you were to run this 10000 times you would see that it eventually settles on a stable amount of memory.

So your example is not a reduced test case of your client-server program.

So if there is an actual memory in your application it is not possible to find it based on that example.

The easiest way to find a leak is to let the program run for a while and then inspect a heap dump with a memory profiler such as yourkit, jprofiler or eclipse's MAT

the8472
  • 40,999
  • 5
  • 70
  • 122