3 votes

FileChannel.write sur Linux produit beaucoup de déchets, mais pas sur Mac

J'essaie de limiter la quantité de déchets produits par ma logithèque, j'ai donc codé un test pour me montrer combien de mémoire est créée par FileChannel.write. Le code ci-dessous alloue ZERO mémoire sur mon Mac, mais crée des tonnes de déchets sur ma machine Linux (Ubuntu 10.04.1 LTS), déclenchant le GC. Les FileChannels sont censés être rapides et légers. Existe-t-il une version de JRE où cela a été amélioré sous 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()));

Les détails de mon JRE sont ci-dessous :

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)

Mis à jour à :

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)

Et ça a bien marché. :-||

Bon, maintenant nous savons que les versions antérieures de FileChannelImpl ont un problème d'allocation de mémoire.

2voto

A.H. Points 23369

Je suis sur Ubuntu 10.04 et je peux confirmer votre observation. Mon JDK est :

    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)

La solution consiste à utiliser un DirectByteBuffer et non un HeapByteBuffer qui est soutenu par un tableau.

Il s'agit d'une très vieille "fonctionnalité" qui remonte au JDK 1.4 si je me souviens bien : Si vous ne donnez pas un DirectByteBuffer a un Channel , puis une DirectByteBuffer Vous voyez essentiellement ces tampons temporaires s'attarder dans la JVM.

Le code suivant fonctionne pour moi :

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

Pour mémoire : La copie de la HeapByteBuffer est pris en

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

qui utilise sun.nio.ch.Util.getTemporaryDirectBuffer(int) . Ce qui, à son tour, met en œuvre un petit pool par thread de DirectByteBuffer s en utilisant SoftReference s. Il n'y a donc pas de véritable fuite de mémoire mais seulement le gaspillage. soupir

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X