2011-09-19 92 views
3

我試圖限制由我的日誌庫產生的垃圾數量,所以我編寫了一個測試來向我顯示FileChannel.write創建了多少內存。下面的代碼在我的Mac上分配ZERO內存,但是在我的Linux機器上(Ubuntu 10.04.1 LTS)創建大量垃圾,觸發GC。 FileChannels應該是快速和輕量級的。有沒有一個JRE版本在Linux上更好?Linux上的FileChannel.write會產生大量的垃圾,但不會在Mac上

File file = new File("fileChannelTest.log"); 
    FileOutputStream fos = new FileOutputStream(file); 
    FileChannel fileChannel = fos.getChannel(); 
    ByteBuffer bb = ByteBuffer.wrap("This is a log line to test!\n".getBytes()); 
    bb.mark(); 
    long freeMemory = Runtime.getRuntime().freeMemory(); 
    for (int i = 0; i < 1000000; i++) { 
     bb.reset(); 
     fileChannel.write(bb); 
    } 
    System.out.println("Memory allocated: " + (freeMemory - Runtime.getRuntime().freeMemory())); 

我JRE的細節如下:

java version "1.6.0_19" 
Java(TM) SE Runtime Environment (build 1.6.0_19-b04) 
Java HotSpot(TM) 64-Bit Server VM (build 16.2-b04, mixed mode) 

更新爲:

java version "1.6.0_27" 
Java(TM) SE Runtime Environment (build 1.6.0_27-b07) 
Java HotSpot(TM) 64-Bit Server VM (build 20.2-b06, mixed mode) 

它工作得很好。 : - |

那麼,現在我們知道FileChannelImpl的早期版本有一個內存分配問題。

+2

當我在linux上使用java 1.6.0_26運行這個時,它說內存分配爲「0」。 –

+0

同樣在這裏:但我在11.04。我沒有10.04測試,對不起。 – Dan

+1

正如一個側面說明,我能夠編寫一個記錄庫,在登錄到磁盤時分配ZERO內存。更多詳細信息,請訪問:http://mentalog.soliveirajr.com – TraderJoeChicago

回答

2

我在Ubuntu 10.04上,我可以確認你的觀察。我的JDK是:

java version "1.6.0_20" 
    OpenJDK Runtime Environment (IcedTea6 1.9.9) (6b20-1.9.9-0ubuntu1~10.04.2) 
    OpenJDK 64-Bit Server VM (build 19.0-b09, mixed mode) 

的解決方案是使用一個DirectByteBuffer,不被由數組支持一個HeapByteBuffer

這是一個很老的「功能」可以追溯到JDK 1.4,如果我沒記錯的話:如果你不給DirectByteBufferChannel,然後臨時DirectByteBuffer分配和寫作前的內容被複制。您基本上可以看到這些臨時緩衝區在JVM中徘徊。

下面的代碼對我的作品:

File file = new File("fileChannelTest.log"); 
    FileOutputStream fos = new FileOutputStream(file); 
    FileChannel fileChannel = fos.getChannel(); 

    ByteBuffer bb1 = ByteBuffer.wrap("This is a log line to test!\n".getBytes()); 

    ByteBuffer bb2 = ByteBuffer.allocateDirect(bb1.remaining()); 
    bb2.put(bb1).flip(); 

    bb2.mark(); 
    long freeMemory = Runtime.getRuntime().freeMemory(); 
    for (int i = 0; i < 1000000; i++) { 
     bb2.reset(); 
     fileChannel.write(bb2); 
    } 
    System.out.println("Memory allocated: " + (freeMemory - Runtime.getRuntime().freeMemory())); 

僅供參考:在HeapByteBuffer的副本採取

sun.nio.ch.IOUtil.write(FileDescriptor, ByteBuffer, long, NativeDispatcher, Object) 

它採用sun.nio.ch.Util.getTemporaryDirectBuffer(int)。這反過來又利用SoftReference s實現了一個小線程池DirectByteBuffer。所以沒有真正的內存泄漏,但只有浪費。 嘆息

+0

帶有堆緩衝區的主要問題是每次都複製整個buffer.remaining(),向套接字發送巨大緩衝區就是這樣。總之,沒有必要使用具有NIO的堆緩衝區,也沒有必要使用具有SSLEngine的直接緩衝區。 – bestsss