web-dev-qa-db-fra.com

Comment ordures recueillir un tampon direct dans Java

J'ai une fuite de mémoire que j'ai isolée à des tampons d'octets directs correctement disposés.

Bytebuffer buff = bytebuffer.allocatedirect (7777777);

Le GC recueille les objets qui abritent ces tampons mais ne disposent pas du tampon lui-même. Si j'instire suffisamment des objets transitoires contenant des tampons, je reçois ce message encourageant:

Java.lang.outofMemoryError: mémoire tampon direct

J'ai cherché ce problème et apparemment

buff.clear ();

et

System.gc ();

ne fonctionnent pas.

27
mglmnc

Je soupçonne que quelque part votre application a une référence à la ou les instances de Bytebuffer et que cela empêche d'être recueilli.

La mémoire tampon pour un bytebuffer direct est allouée en dehors du tas normal (de sorte que le GC ne le déplace pas !!). Cependant, l'API de BYTEBUFFER ne fournit aucune méthode pour éliminer explicitement/transposer un tampon. Je suppose donc que le collectionneur des ordures le fera ... Une fois qu'il détermine que l'objet BYTEBUFFER n'est plus référencé.

16
Stephen C

La DBB sera traitée une fois qu'elle frappe la file d'attente de référence et le finaliseur est exécuté. Cependant, comme nous ne pouvons pas dépendre d'un finisseur à exécuter, nous pouvons utiliser la réflexion pour appeler manuellement son "nettoyeur".

Utilisation de la réflexion:

/**
* DirectByteBuffers are garbage collected by using a phantom reference and a
* reference queue. Every once a while, the JVM checks the reference queue and
* cleans the DirectByteBuffers. However, as this doesn't happen
* immediately after discarding all references to a DirectByteBuffer, it's
* easy to OutOfMemoryError yourself using DirectByteBuffers. This function
* explicitly calls the Cleaner method of a DirectByteBuffer.
* 
* @param toBeDestroyed
*          The DirectByteBuffer that will be "cleaned". Utilizes reflection.
*          
*/
public static void destroyDirectByteBuffer(ByteBuffer toBeDestroyed)
    throws IllegalArgumentException, IllegalAccessException,
    InvocationTargetException, SecurityException, NoSuchMethodException {

  Preconditions.checkArgument(toBeDestroyed.isDirect(),
      "toBeDestroyed isn't direct!");

  Method cleanerMethod = toBeDestroyed.getClass().getMethod("cleaner");
  cleanerMethod.setAccessible(true);
  Object cleaner = cleanerMethod.invoke(toBeDestroyed);
  Method cleanMethod = cleaner.getClass().getMethod("clean");
  cleanMethod.setAccessible(true);
  cleanMethod.invoke(cleaner);

}
16
Li Pi

Le ByteBuffer Documentation dit:

Un tampon d'octet direct peut être créé en invoquant le allocateDirect méthode d'usine de cette classe. Les tampons retournés par cette méthode ont généralement des coûts d'allocation et de distribution quelque peu plus élevés que les tampons non directs. Le contenu des tampons directs peut résider en dehors du tas de déchets normaux et leur impact sur l'empreinte de mémoire d'une application pourrait ne pas être évidente. Il est donc recommandé que des tampons directs soient alloués principalement aux grands tampons de longue durée qui sont soumis aux opérations d'E/S du système sous-jacentes. En général, il est préférable d'attribuer des tampons directs uniquement lorsqu'ils produisent un gain mesurable dans la performance du programme.

En particulier, la déclaration "peut résider en dehors du tas de déchets normaux" semble pertinente pour votre exemple.

12
Greg Hewgill

La mémoire allouée est réalisée à travers une liberge indigène. Cette mémoire sera libérée lorsque la méthode de finalisation de BYTEBUFFER # est appelée, IAW lorsque le tampon est GC'D. Regardez l'allocation () et finaliser () les implémentations de directbytebufferimpl .

buff.clear() n'est pas nécessaire, System.gc() _ Aide que si, comme les autres déjà mentionnés, il n'y a plus de référence à l'objet BYTEBUFFER.

5
Andreas_D

Tant que vous comptez sur la mise en œuvre spécifique Sun (Oracle), un meilleur choix que d'essayer de changer la visibilité de Java.nio.directbytebuffer consiste à utiliser l'interface Sun.nio.ch.DirectBer via des réflexions.

/**
 * Sun specific mechanisms to clean up resources associated with direct byte buffers.
 */
@SuppressWarnings("unchecked")
private static final Class<? extends ByteBuffer> Sun_DIRECT_BUFFER = (Class<? extends ByteBuffer>) lookupClassQuietly("Sun.nio.ch.DirectBuffer");

private static final Method Sun_BUFFER_CLEANER;

private static final Method Sun_CLEANER_CLEAN;

static
{
    Method bufferCleaner = null;
    Method cleanerClean = null;
    try
    {
        // operate under the assumption that if the Sun direct buffer class exists,
        // all of the Sun classes exist
        if (Sun_DIRECT_BUFFER != null)
        {
            bufferCleaner = Sun_DIRECT_BUFFER.getMethod("cleaner", (Class[]) null);
            Class<?> cleanClazz = lookupClassQuietly("Sun.misc.Cleaner");
            cleanerClean = cleanClazz.getMethod("clean", (Class[]) null);
        }
    }
    catch (Throwable t)
    {
        t.printStackTrace();
    }
    Sun_BUFFER_CLEANER = bufferCleaner;
    Sun_CLEANER_CLEAN = cleanerClean;
}

public static void releaseDirectByteBuffer(ByteBuffer buffer)
{
    if (Sun_DIRECT_BUFFER != null && Sun_DIRECT_BUFFER.isAssignableFrom(buffer.getClass()))
    {
        try
        {
            Object cleaner = Sun_BUFFER_CLEANER.invoke(buffer, (Object[]) null);
            Sun_CLEANER_CLEAN.invoke(cleaner, (Object[]) null);
        }
        catch (Throwable t)
        {
            logger.trace("Exception occurred attempting to clean up Sun specific DirectByteBuffer.", t);
        }
    }
}
1
Brett Okken