线程结束工作后,JVM不释放字节数组的内存

时间:2015-03-29 10:50:46

标签: java memory garbage-collection jvm

我有通过jvm释放内存的问题。我知道java的释放内存在线程从run方法退出后会被重新调整。当其他对象没有引用时会被垃圾回收器删除,例如windows / frames等。为什么在下面的代码gc不释放字节数组的内存尽管线程结束他们的工作?我知道System.gc()只是gc的建议,但是我只是为了以防万一,并且为字节数组引用分配null是不必要的。
下面的代码只是示例,在我的客户端 - 服务器应用程序中,当服务器向客户端发送文件时,我遇到了真正的问题。

 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();     
} 

我留下上面的问题并且变得实用。之前有将整个文件加载到一个字节数组并使用writeObject()发送它,但它会导致内存问题。看看那段代码:

                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);

它是接收文件的客户端。收到最后一个数据包后,在第一个文件中读取METHOD METHOD BLOCKS。 这是服务器端:

       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();             
        }    

任何想法如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

从Windows任务管理器获取的信息并不意味着很多。 JVM根据许多因素动态调整堆的大小,吞吐量是首要考虑因素。堆大小永远不会完全等于可达对象分配的实际内存。

如果要观察垃圾收集的影响,请使用VisualVM连接到JVM。为了获得最佳效果,请安装VisualGC插件并打开其选项卡,您可以在那里实时观察所有世代的大小变化。垃圾收集将立即反映在显示的占用率中,您还可以注意到堆本身的大小调整(很少)。

答案 1 :(得分:0)

  

for(int i = 0; i&lt; 5; i ++){

这很可能是GC没有足够的时间来稳定堆大小的稳定状态。如果你要运行10000次,你会发现它最终会稳定在一个稳定的内存中。

因此,您的示例不是客户端 - 服务器程序的简化测试用例。

因此,如果您的应用程序中存在实际内存,则无法根据该示例找到它。

找到泄漏的最简单方法是让程序运行一段时间,然后使用内存分析器检查堆转储,例如yourkitjprofilereclipse's MAT