我试图限制日志库产生的垃圾量,所以我编写了一个测试代码,向我展示FileChannel.write创建多少内存。以下代码在Mac上分配了零内存,但在Linux机器(Ubuntu 10.04.1 LTS)上创建了大量垃圾,从而触发了GC。 FileChannel应该是快速且轻量级的。是否有JRE版本在Linux上做得更好?
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的早期版本存在内存分配问题。
最佳答案
我正在使用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:如果您不给
DirectByteBuffer
加上Channel
,那么将分配一个临时的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)
。进而使用DirectByteBuffer
实现了一个每个SoftReference
的每个线程池。因此,没有真正的内存泄漏,只有浪费。叹