FileChannel.write в Linux производит много мусора, но не в Mac - PullRequest
3 голосов
/ 19 сентября 2011

Я пытаюсь ограничить количество мусора, производимого моей библиотекой журналов, поэтому я написал код теста, чтобы показать, сколько памяти создает FileChannel.write.Приведенный ниже код распределяет память ZERO на моем Mac, но создает кучу мусора на моем Linux-компьютере (Ubuntu 10.04.1 LTS), вызывая GC.FileChannels должен быть быстрым и легким.Есть ли версия 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 есть проблема выделения памяти.

1 Ответ

2 голосов
/ 19 сентября 2011

Я нахожусь на 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 с.Таким образом, нет никакой реальной утечки памяти, а только растрата. Вздох

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...