2011-02-09 8 views
26

Duplicar posibles:
Creating a memory leak with Java¿La forma más fácil de causar pérdida de memoria en Java?

¿Cuál es la forma más fácil de provocar una pérdida de memoria de Java?

+0

¿Está buscando un ejemplo artificial o un error de programación muy común? – mikerobi

+0

un ejemplo artificial sería lo mejor por favor. –

+0

Se crea una pérdida de memoria cuando un objeto que no está destinado a ser utilizado tiene una referencia. Casi cualquier programa que uno podría escribir sería un ejemplo artificial de una pérdida de memoria. – OrangeDog

Respuesta

27

realmente no se puede "pérdida de memoria" en Java a menos que usted:

  • cadenas internar
  • generar clases de memoria
  • fuga en el código nativo llamado por JNI
  • mantener las referencias a cosas que usted no quiero en un lugar olvidado u oscuro.

Supongo que estás interesado en el último caso. Los escenarios comunes son:

  • oyentes, especialmente hecho con clases internas
  • cachés.

Un buen ejemplo sería:

  • Crear una interfaz gráfica oscilación que pone en marcha un número potencialmente ilimitado de ventanas modales;
  • tienen la ventana modal hacer algo como esto durante su inicialización:
     
    StaticGuiHelper.getMainApplicationFrame().getOneOfTheButtons().addActionListener(new ActionListener(){ 
        public void actionPerformed(ActionEvent e){ 
        // do nothing... 
        } 
    }) 
    

La acción registrada no hace nada, pero hará que la ventana modal para permanecer en la memoria para siempre, incluso después de cerrar, causando una fuga - ya que los oyentes nunca se anulan y cada objeto de clase interna anónimo contiene una referencia (invisible) a su objeto externo. Además, cualquier objeto al que se haga referencia desde las ventanas modales también tiene la posibilidad de tener una fuga.

Es por eso que las bibliotecas como EventBus utilizan referencias débiles por defecto.

Además de los oyentes, otros ejemplos típicos son los cachés, pero no puedo pensar en un buen ejemplo.

+4

Las cadenas internas no son realmente pérdidas de memoria, también se pueden recolectar basura. El problema es solo que (en las implementaciones habituales) se encuentran en un área de memoria especial (PermGen) que es más pequeña que el resto de la memoria y, por lo tanto, se llena más fácilmente. –

+0

Tienes razón. Las cadenas internas no son una fuga "real" (de nuevo, "fugas reales" no son posibles con jvm). Sin embargo, la permanente se recopila solo cuando todo lo demás falla y su contenido sobrevive a las colecciones principales, por lo que es una de las pocas fuentes de problemas reales de memoria. También las cadenas internas toman espacio a pesar de que no están referenciadas desde su programa. En ese sentido, están lo más cerca posible de una fuga. – fdreger

+0

Una de las mejores respuestas: https://www.reddit.com/r/AMA/comments/7z8tw0/people_who_are_experts_in_their_fieldhobby_ama/dumd8y8/ –

3
  1. Crear una colección de objetos en el ámbito de la clase
  2. periódicamente añadir nuevos objetos a la colección
  3. No deje caer la referencia a la instancia de la clase que contiene la colección

Debido a que hay siempre es una referencia a la colección y la instancia del objeto que posee la colección el Garbage Collector nunca limpiará esa memoria, lo que ocasionará una "fuga" con el tiempo.

8
public static List<byte[]> list = new ArrayList<byte[]>(); 

Y a continuación, agregue arreglos (grandes) sin eliminarlos. En algún momento se le agotará la memoria sin sospechar. (Puede hacer esto con cualquier objeto, pero con arreglos grandes y completos puede agotar la memoria más rápido)

En Java, si desreferencia un objeto (se sale del alcance), se recoge basura. Entonces debe mantener una referencia para tener un problema de memoria.

+0

Esto hará que se quede sin memoria, pero ¿cómo puede tener una fuga si nunca hace nada para romper una referencia de objeto? – mikerobi

+5

@mikerobi - una pérdida de memoria es cuando "ocupa" algo de memoria sin limpiarla (y sin usarla). Sin embargo, si desreferencia el objeto, será basura recolectada. – Bozho

+1

Entiendo eso, pero no considero que esto sea una filtración en todos los casos. Definitivamente se trata de una pérdida si, erróneamente, hace que una variable de clase sea estática, posiblemente sea una pérdida si la está utilizando como un valor global en un proceso de larga ejecución. No es una pérdida si su intención es que los datos persistan hasta la terminación del programa. El hecho de que un bucle infinito agote tu memoria no tiene nada que ver con el hecho de que se trata de una fuga. Una gran cantidad de filtraciones no se notan, a menos que continuamente asignen nuevos datos, pero tener un trozo fijo de memoria huérfana sigue siendo una filtración. – mikerobi

8

Aquí está un ejemplo sencillo

public class finalizer { 
    @Override 
     protected void finalize() throws Throwable { 
     while (true) { 
      Thread.yield(); 
     } 
    } 

    public static void main(String[] args) { 
     while (true) { 
      for (int i = 0; i < 100000; i++) { 
       finalizer f = new finalizer(); 
      } 

      System.out.println("" + Runtime.getRuntime().freeMemory() + " bytes free!"); 
     } 
    } 
} 
+0

¿Podría explicarnos un poco cómo está logrando la fuga de memoria en este ejemplo? – TheBlueNotebook

+0

No estoy seguro pero este código parece funcionar, al menos mató a mi pc y los procesos quedaron en segundo plano incluso después de cerrar eclipse – pescamillam

+1

@TheBlueNotebook El método finalizado que reemplazó es lo que Java normalmente llama cuando está a punto de liberar memoria para un objeto. En su método principal crea 100K de finalizadores y luego le dice a la JVM que libere toda la memoria. La JVM hace esto cortésmente y llama a finalizar antes de liberar realmente la memoria. El método de finalización que llama cede para siempre, por lo que los objetos nunca se eliminan, pero el bucle principal continúa, creando así 100K Objetos que nunca serán eliminados, luego otro, luego otro ... – Jeutnarg

3

Por lo que he leído en la respuesta más votada, que está muy probablemente para solicitar una pérdida de memoria C-similares. Bueno, como hay una colección de garbagge, no se puede asignar un objeto, perder todas sus referencias y conseguir que siga ocupando memoria, eso sería un serio error de JVM.

Por otro lado, puede pasar hilos de fuga, lo que, por supuesto, causaría este estado, ya que podría ejecutar algún subproceso con sus referencias a objetos, y puede perder la referencia del subproceso. Todavía se puede obtener la referencia del hilo a través de la API - http://www.exampledepot.com/egs/java.lang/ListThreads.html

0

probar este clase simple:

public class Memory { 
private Map<String, List<Object>> dontGarbageMe = new HashMap<String, List<Object>>(); 

public Memory() { 
    dontGarbageMe.put("map", new ArrayList<Object>()); 
} 

public void useMemInMB(long size) { 
    System.out.println("Before=" + getFreeMemInMB() + " MB"); 

    long before = getFreeMemInMB(); 
    while ((before - getFreeMemInMB()) < size) { 
     dontGarbageMe.get("map").add("aaaaaaaaaaaaaaaaaaaaaa"); 
    } 

    dontGarbageMe.put("map", null); 

    System.out.println("After=" + getFreeMemInMB() + " MB"); 
} 

private long getFreeMemInMB() { 
    return Runtime.getRuntime().freeMemory()/(1024 * 1024); 
} 

public static void main(String[] args) { 
    Memory m = new Memory(); 
    m.useMemInMB(15); // put here apropriate huge value 
} 
} 
+0

Este es el ejemplo simple más complicado aquí. ;) –

+0

¿Dónde está la fuga? no es la lista liberada después de GC? –

15

"una pérdida de memoria, en la informática (o fuga, en este contexto), se produce cuando un programa informático consume memoria pero no puede volver a liberarla en el sistema operativo ". (Wikipedia)

La respuesta fácil es: No se puede. Java hace gestión automática de memoria y liberará recursos que no son necesarios para usted. No puedes evitar que esto suceda. SIEMPRE podrá liberar los recursos. En los programas con gestión de memoria manual, esto es diferente. No puedes obtener algo de memoria en C usando malloc(). Para liberar la memoria, necesita el puntero que devuelve malloc y llamar a free() en él. Pero si ya no tiene el puntero (sobrescrito, o se excedió la vida útil), desafortunadamente no puede liberar esta memoria y, por lo tanto, tiene una pérdida de memoria.

Todas las otras respuestas hasta ahora están en mi definición, en realidad no son fugas de memoria. Todos apuntan a llenar la memoria con cosas sin sentido muy rápido. Pero en cualquier momento, aún se podía desreferenciar los objetos que se crearon y liberar así la memoria -> SIN FUGAS.Sin embargo, la respuesta de acconrad es bastante aproximada, como tengo que admitir, ya que su solución es simplemente "colgar" al recolector de basura al forzarlo en un bucle infinito).

La respuesta larga es: Puede obtener una pérdida de memoria escribiendo una biblioteca para Java utilizando el JNI, que puede tener administración de memoria manual y, por lo tanto, tener pérdidas de memoria. Si llama a esta biblioteca, su proceso java perderá memoria. O bien, puede tener errores en la JVM, de modo que la JVM pierda memoria. Probablemente hay errores en la JVM, incluso puede haber algunos conocidos, ya que la recolección de basura no es tan trivial, pero sigue siendo un error. Por diseño esto no es posible. Es posible que solicite un código Java que se vea afectado por dicho error. Lo siento, no conozco uno y, de todos modos, podría no ser un error en la próxima versión de Java.

+1

"Pero en cualquier momento aún se podía desreferenciar los objetos que se crearon y así liberar la memoria". Estoy en desacuerdo. El implementador de clase puede ocultar los identificadores de objeto del mundo exterior. –

+0

@trinithis: si tiene un objeto que desperdicia memoria de forma privada asignando grandes cantidades de memoria, entonces no puede obligar al objeto a liberar la memoria sin descartar ese objeto también. Pero en este caso, sigue desperdiciando memoria y no una fuga. La memoria PUEDE ser liberada. Se liberará una vez que ya no se haga referencia al objeto que hace referencia a la memoria desperdiciada. ¿O te malentendí? – yankee

+0

Creo que entendí mal lo que quería decir con "desreferencia". Estaba pensando en el significado de C de la palabra. –

0

Parece que la mayoría de las respuestas no son fugas de memoria de estilo C.

Pensé que agregaría un ejemplo de una clase de biblioteca con un error que le dará una excepción de falta de memoria. De nuevo, no es una verdadera pérdida de memoria, pero es un ejemplo de algo que se está quedando sin memoria que usted no esperaría.

public class Scratch { 
    public static void main(String[] args) throws Exception { 
     long lastOut = System.currentTimeMillis(); 
     File file = new File("deleteme.txt"); 

     ObjectOutputStream out; 
     try { 
      out = new ObjectOutputStream(
        new FileOutputStream("deleteme.txt")); 

      while (true) { 
       out.writeUnshared(new LittleObject()); 
       if ((System.currentTimeMillis() - lastOut) > 2000) { 
        lastOut = System.currentTimeMillis(); 
        System.out.println("Size " + file.length()); 
        // out.reset(); 
       } 
      } 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 

class LittleObject implements Serializable { 
    int x = 0; 
} 

Encontrará el código original y descripción del error en

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4363937

0

La siguiente clase extremadamente artificial Box perderá memoria si se utiliza. Los objetos que son put en esta clase son eventualmente (después de otra llamada a put para ser precisos ... siempre que el mismo objeto no sea re- put) inaccesibles para el mundo exterior. No se pueden desreferenciar a través de esta clase, sin embargo, esta clase garantiza que no puedan recopilarse. Esta es una fuga REAL. Sé que esto es realmente artificial, pero es posible hacer casos similares por accidente.

import java.util.ArrayList; 
import java.util.Collection; 
import java.util.Stack; 

public class Box <E> { 
    private final Collection<Box<?>> createdBoxes = new ArrayList<Box<?>>(); 
    private final Stack<E> stack = new Stack<E>(); 

    public Box() { 
     createdBoxes.add(this); 
    } 

    public void put (E e) { 
     stack.push(e); 
    } 

    public E get() { 
     if (stack.isEmpty()) { 
      return null; 
     } 
     return stack.peek(); 
    } 
} 
Cuestiones relacionadas