Creare una perdita di memoria con Java

Ho appena avuto un’intervista e mi è stato chiesto di creare una perdita di memoria con Java. Inutile dire che mi sono sentito piuttosto stupido non avendo idea di come iniziare a crearne uno.

Quale sarebbe un esempio?

Ecco un buon modo per creare una vera perdita di memoria (oggetti inaccessibili eseguendo il codice ma ancora archiviati in memoria) in puro Java:

  1. L’applicazione crea un thread di lunga durata (o utilizza un pool di thread per perdite ancora più veloci).
  2. Il thread carica una class tramite un ClassLoader (opzionale).
  3. La class alloca un grosso blocco di memoria (ad es. new byte[1000000] ), memorizza un riferimento forte ad esso in un campo statico e quindi memorizza un riferimento a se stesso in un ThreadLocal. L’allocazione della memoria extra è facoltativa (la perdita dell’istanza di class è sufficiente), ma renderà la perdita più veloce.
  4. Il thread cancella tutti i riferimenti alla class personalizzata o al ClassLoader da cui è stato caricato.
  5. Ripetere.

Questo funziona perché ThreadLocal mantiene un riferimento all’object, che mantiene un riferimento alla sua Classe, che a sua volta conserva un riferimento al suo ClassLoader. ClassLoader, a sua volta, mantiene un riferimento a tutte le Classi che ha caricato.

(E ‘stato peggio in molte implementazioni JVM, specialmente prima di Java 7, perché Classi e ClassLoaders sono stati allocati direttamente in permgen e non sono mai stati GC. Tuttavia, indipendentemente da come la JVM gestisce lo scaricamento della class, un ThreadLocal impedirà comunque un Oggetto di class dall’essere recuperato.)

Una variazione su questo modello è il motivo per cui i contenitori di applicazioni (come Tomcat) possono perdere memoria come un setaccio se si ricolloca spesso le applicazioni che utilizzano ThreadLocals in qualche modo. (Poiché il contenitore dell’applicazione utilizza Thread come descritto e ogni volta che si ridistribuisce l’applicazione viene utilizzato un nuovo ClassLoader.)

Aggiornamento : dal momento che molte persone continuano a chiederlo, ecco un esempio di codice che mostra questo comportamento in azione .

Riferimento dell’object statico contenente il campo [esp finale field]

 class MemorableClass { static final ArrayList list = new ArrayList(100); } 

Chiama String.intern() sulla stringa lunga

 String str=readString(); // read lengthy string any source db,textbox/jsp etc.. // This will place the string in memory pool from which you can't remove str.intern(); 

(Unclosed) open stream (file, rete ecc …)

 try { BufferedReader br = new BufferedReader(new FileReader(inputFile)); ... ... } catch (Exception e) { e.printStacktrace(); } 

Connessioni non chiuse

 try { Connection conn = ConnectionFactory.getConnection(); ... ... } catch (Exception e) { e.printStacktrace(); } 

Aree irraggiungibili dal garbage collector di JVM , ad esempio memoria allocata tramite metodi nativi

Nelle applicazioni Web, alcuni oggetti vengono archiviati nell’ambito dell’applicazione finché l’applicazione non viene arrestata o rimossa esplicitamente.

 getServletContext().setAttribute("SOME_MAP", map); 

Opzioni JVM errate o inappropriate , come l’opzione noclassgc su IBM JDK che impedisce la garbage collection della class inutilizzata

Vedi Impostazioni jdk di IBM .

Una cosa semplice da fare è usare un HashSet con un hashCode() o equals() inesatto (o inesistente) e quindi continuare ad aggiungere “duplicati”. Invece di ignorare i duplicati come dovrebbe, il set crescerà sempre e non sarai in grado di rimuoverli.

Se vuoi aggirare queste chiavi / elementi sbagliati puoi usare un campo statico come

 class BadKey { // no hashCode or equals(); public final String key; public BadKey(String key) { this.key = key; } } Map map = System.getProperties(); map.put(new BadKey("key"), "value"); // Memory leak even if your threads die. 

Di seguito ci sarà un caso non ovvio in cui Java perde, oltre al caso standard di ascoltatori dimenticati, riferimenti statici, chiavi false / modificabili nelle hashmap o solo thread bloccati senza alcuna possibilità di terminare il loro ciclo di vita.

  • File.deleteOnExit() – perde sempre la stringa, se la stringa è una sottostringa, la perdita è ancora peggiore (viene anche trapelata la variabile sottostante [] nella sottostringa Java 7 copia anche il char[] , quindi la versione successiva non si applica ; @ Daniel, non c’è bisogno di voti, però.

Mi concentrerò sui thread per mostrare il pericolo di thread non gestiti per lo più, non voglio nemmeno toccare swing.

  • Runtime.addShutdownHook e non rimuovere … e quindi anche con removeShutdownHook a causa di un bug nella class ThreadGroup riguardante i thread non avviati che potrebbero non essere raccolti, in modo efficace perdono il ThreadGroup. JGroup ha la perdita in GossipRouter.

  • Creando, ma non avviando, un Thread entra nella stessa categoria di cui sopra.

  • La creazione di un thread eredita ContextClassLoader e ContextClassLoader , oltre a ThreadGroup e qualsiasi InheritedThreadLocal , tutti i riferimenti sono potenziali perdite, insieme alle intere classi caricate dal classloader e tutti i riferimenti statici e ja-ja. L’effetto è particolarmente visibile con l’intero framework jucExecutor che presenta un’interfaccia ThreadFactory molto semplice, ma la maggior parte degli sviluppatori non ha idea del pericolo in agguato. Inoltre molte librerie avviano i thread su richiesta (troppe librerie popolari di settore).

  • ThreadLocal ; quelli sono cattivi in ​​molti casi. Sono sicuro che tutti hanno visto un po ‘di semplici cache basate su ThreadLocal, beh, le cattive notizie: se il thread continua ad andare oltre le aspettative la vita del contesto ClassLoader, si tratta di una pura piccola perdita. Non utilizzare le cache ThreadLocal se non strettamente necessario.

  • Chiamando ThreadGroup.destroy() quando ThreadGroup non ha thread in sé, ma mantiene comunque ThreadGroups figlio. Una perdita grave che impedirà al ThreadGroup di rimuoverlo dal suo genitore, ma tutti i bambini diventano non enumerabili.

  • Usando WeakHashMap e il valore (in) fa direttamente riferimento alla chiave. Questo è difficile da trovare senza una discarica di heap. Questo vale per tutti i Weak/SoftReference che potrebbero mantenere un riferimento rigido all’object protetto.

  • Utilizzando java.net.URL con il protocollo HTTP (S) e caricando la risorsa da (!). Questo è speciale, KeepAliveCache crea un nuovo thread nel sistema ThreadGroup che perde il classloader di contesto corrente del thread. Il thread viene creato alla prima richiesta quando non esiste alcun thread attivo, quindi potresti essere fortunato o semplicemente perdere. La perdita è già stata risolta in Java 7 e il codice che crea il thread rimuove correttamente il classloader di contesto. Ci sono pochi altri casi ( come ImageFetcher , anche risolto ) di creare thread simili.

  • Utilizzo di InflaterInputStream passando il new java.util.zip.Inflater() nel costruttore (per esempio PNGImageDecoder ) e non chiamando end() del filtro. Bene, se passi il costruttore con solo new , nessuna possibilità … E sì, chiamare close() sul stream non chiude il gonfiatore se viene passato manualmente come parametro costruttore. Questa non è una vera perdita dal momento che sarebbe stata rilasciata dal finalizzatore … quando lo riterrà necessario. Fino a quel momento mangia così male la memoria nativa che può causare a Linux oom_killer di uccidere il processo impunemente. Il problema principale è che la finalizzazione in Java è molto inaffidabile e G1 ha peggiorato la situazione fino alla 7.0.2. Morale della trama: rilascia le risorse native non appena puoi; il finalizzatore è troppo povero.

  • Lo stesso caso con java.util.zip.Deflater . Questo è molto peggiore da quando Deflater è affamato di memoria in Java, cioè usa sempre 15 bit (max) e 8 livelli di memoria (9 è massimo) allocando diverse centinaia di KB di memoria nativa. Fortunatamente, Deflater non è ampiamente utilizzato e, a mia conoscenza, JDK non contiene abusi. Chiamare sempre end() se si crea manualmente un Deflater o Inflater . La parte migliore degli ultimi due: non è ansible trovarli tramite normali strumenti di profilazione disponibili.

(Posso aggiungere altri perditempo di tempo che ho incontrato su richiesta).

Buona fortuna e stai al sicuro; le perdite sono malvagie!

La maggior parte degli esempi qui sono “troppo complessi”. Sono casi limite. Con questi esempi, il programmatore ha commesso un errore (come non ridefinire gli equals / hashcode), o è stato morso da un caso angolo della JVM / JAVA (carico di class con static …). Penso che non sia il tipo di esempio che vuole un intervistatore o il caso più comune.

Ma ci sono casi davvero più semplici per perdite di memoria. Il garbage collector libera solo ciò che non è più referenziato. Noi come sviluppatori Java non ci interessa la memoria. Lo allociamo quando necessario e lo lasciamo automaticamente libero. Belle.

Ma qualsiasi applicazione di lunga durata tende ad avere uno stato condiviso. Può essere qualsiasi cosa, statica, singleton … Spesso le applicazioni non banali tendono a creare grafici di oggetti complessi. Solo dimenticare di impostare un riferimento a null o più spesso dimenticando di rimuovere un object da una raccolta è sufficiente per creare una perdita di memoria.

Ovviamente tutti i tipi di ascoltatori (come gli ascoltatori dell’interfaccia utente), le cache o qualsiasi stato condiviso di lunga durata tendono a produrre perdite di memoria se non gestite correttamente. Ciò che deve essere compreso è che questo non è un caso angular Java o un problema con il garbage collector. È un problema di progettazione. Progettiamo di aggiungere un listener a un object longevo, ma non rimuoviamo il listener quando non è più necessario. Memorizziamo gli oggetti nella cache, ma non abbiamo alcuna strategia per rimuoverli dalla cache.

Forse abbiamo un grafico complesso che memorizza lo stato precedente necessario per un calcolo. Ma lo stato precedente è esso stesso collegato allo stato prima e così via.

Come se dovessimo chiudere connessioni o file SQL. Dobbiamo impostare i riferimenti appropriati su null e rimuovere gli elementi dalla raccolta. Avremo strategie di caching adeguate (dimensione massima della memoria, numero di elementi o timer). Tutti gli oggetti che consentono a un listener di essere avvisati devono fornire sia un metodo addListener che removeListener. E quando questi notificatori non vengono più utilizzati, devono cancellare la loro lista di ascoltatori.

Una perdita di memoria è davvero ansible ed è perfettamente prevedibile. Non sono necessarie caratteristiche linguistiche speciali o casi angolari. Le perdite di memoria sono o un indicatore che qualcosa è forse mancante o anche di problemi di progettazione.

La risposta dipende interamente da ciò che l’intervistatore pensava di chiedere.

È ansible in pratica fare fuoriuscire Java? Certo che lo è, e ci sono molti esempi nelle altre risposte.

Ma ci sono più meta-domande che potrebbero essere state poste?

  • Un’implementazione Java teoricamente “perfetta” è vulnerabile alle perdite?
  • Il candidato comprende la differenza tra teoria e realtà?
  • Il candidato capisce come funziona la garbage collection?
  • O come si suppone che la raccolta dei rifiuti funzioni in un caso ideale?
  • Sanno che possono chiamare altre lingue attraverso interfacce native?
  • Sanno di perdere memoria in quelle altre lingue?
  • Il candidato sa anche cosa sia la gestione della memoria e cosa sta succedendo dietro la scena in Java?

Sto leggendo la tua meta-domanda come “Qual è la risposta che avrei potuto usare in questa situazione di intervista”. E quindi, mi concentrerò sulle abilità di intervista invece di Java. Credo che sia più probabile che tu ripeta la situazione di non conoscere la risposta a una domanda in un’intervista di quanto tu non sia nel posto in cui devi sapere come fare a fugare Java. Quindi, si spera, questo aiuterà.

Una delle abilità più importanti che puoi sviluppare per intervistare è imparare ad ascoltare triggersmente le domande e lavorare con l’intervistatore per estrarre le loro intenzioni. Ciò non solo ti consente di rispondere alla domanda nel modo desiderato, ma mostra anche che hai delle capacità comunicative vitali. E quando si tratta di scegliere tra molti sviluppatori di talento, assumerò quello che ascolta, pensa e capisce prima che rispondano ogni volta.

Quello che segue è un esempio piuttosto inutile, se non capisci JDBC . O almeno come JDBC si aspetta che uno sviluppatore chiuda le istanze di Connection , Statement e ResultSet prima di scartarle o perdere riferimenti ad esse, invece di affidarsi all’implementazione di finalize .

 void doWork() { try { Connection conn = ConnectionFactory.getConnection(); PreparedStatement stmt = conn.preparedStatement("some query"); // executes a valid query ResultSet rs = stmt.executeQuery(); while(rs.hasNext()) { ... process the result set } } catch(SQLException sqlEx) { log(sqlEx); } } 

Il problema con quanto sopra è che l’object Connection non è chiuso e quindi la connessione fisica rimarrà aperta, finché il garbage collector non si avvicina e vede che non è raggiungibile. GC invocherà il metodo finalize , ma esistono driver JDBC che non implementano la finalize , almeno non nello stesso modo in cui è implementato Connection.close . Il comportamento risultante è che mentre la memoria verrà recuperata a causa di oggetti irraggiungibili raccolti, le risorse (inclusa la memoria) associate all’object Connection potrebbero semplicemente non essere recuperate.

In un evento del genere in cui il metodo finalize Connection non ripulisce tutto, si potrebbe effettivamente scoprire che la connessione fisica al server del database durerà diversi cicli di garbage collection, finché il server del database non capirà che la connessione non è triggers ( se lo fa), e dovrebbe essere chiuso.

Anche se il driver JDBC doveva implementare la finalize , è ansible che vengano lanciate eccezioni durante la finalizzazione. Il comportamento risultante è che qualsiasi memoria associata all’object “dormiente” ora non sarà recuperata, poiché è garantito che il finalize venga richiamato una sola volta.

Lo scenario sopra riportato relativo alle eccezioni durante la finalizzazione degli oggetti è correlato a un altro scenario che potrebbe portare a una perdita di memoria: la risurrezione degli oggetti. La resurrezione dell’object viene spesso eseguita intenzionalmente creando un forte riferimento all’object da essere finalizzato, da un altro object. Quando la risurrezione dell’object viene utilizzata in modo errato, si verificherà una perdita di memoria in combinazione con altre fonti di perdite di memoria.

Ci sono molti altri esempi che puoi evocare – come

  • Gestire un’istanza di List cui si sta solo aggiungendo all’elenco e non eliminando da esso (sebbene sia necessario eliminare elementi che non sono più necessari) o
  • Aprendo Socket o File , ma non chiudendoli quando non sono più necessari (simile all’esempio sopra che riguarda la class Connection ).
  • Non scaricare Singletons quando si abbassa un’applicazione Java EE. Apparentemente, il Classloader che ha caricato la class singleton manterrà un riferimento alla class, e quindi l’istanza singleton non verrà mai raccolta. Quando viene distribuita una nuova istanza dell’applicazione, viene generalmente creato un nuovo programma di caricamento classi e il precedente programma di caricamento classi continuerà ad esistere a causa del singleton.

Probabilmente uno degli esempi più semplici di una potenziale perdita di memoria, e come evitarlo, è l’implementazione di ArrayList.remove (int):

 public E remove(int index) { RangeCheck(index); modCount++; E oldValue = (E) elementData[index]; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index + 1, elementData, index, numMoved); elementData[--size] = null; // (!) Let gc do its work return oldValue; } 

Se lo stavi implementando da solo, avresti pensato di cancellare l’elemento dell’array che non è più usato ( elementData[--size] = null )? Quel riferimento potrebbe mantenere vivo un enorme object …

Ogni volta che mantieni i riferimenti intorno agli oggetti che non ti servono più hai una perdita di memoria. Consulta Gestione delle perdite di memoria nei programmi Java per esempi di come le perdite di memoria si manifestano in Java e cosa puoi fare al riguardo.

Sei in grado di fare una perdita di memoria con la class sun.misc.Unsafe . In realtà questa class di servizio viene utilizzata in diverse classi standard (ad esempio nelle classi java.nio ). Non è ansible creare istanze di questa class direttamente , ma è ansible utilizzare la riflessione per farlo .

Il codice non viene compilato in Eclipse IDE – compila usando il comando javac (durante la compilazione riceverai avvertimenti)

 import java.lang.reflect.Constructor; import java.lang.reflect.Field; import sun.misc.Unsafe; public class TestUnsafe { public static void main(String[] args) throws Exception{ Class unsafeClass = Class.forName("sun.misc.Unsafe"); Field f = unsafeClass.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafe unsafe = (Unsafe) f.get(null); System.out.print("4..3..2..1..."); try { for(;;) unsafe.allocateMemory(1024*1024); } catch(Error e) { System.out.println("Boom :)"); e.printStackTrace(); } } } 

Posso copiare la mia risposta da qui: il modo più semplice per causare perdita di memoria in Java?

“Una perdita di memoria, in informatica (o perdita, in questo contesto), si verifica quando un programma per computer consuma memoria, ma non è in grado di rilasciarlo di nuovo al sistema operativo.” (Wikipedia)

La risposta facile è: non puoi. Java esegue la gestione automatica della memoria e libererà risorse che non sono necessarie per te. Non puoi impedire che ciò accada. Sarà SEMPRE in grado di rilasciare le risorse. Nei programmi con gestione manuale della memoria, questo è diverso. Si può ottenere un po ‘di memoria in C usando malloc (). Per liberare la memoria, è necessario il puntatore che malloc ha restituito e chiama free () su di esso. Ma se non si ha più il puntatore (sovrascritto o superato per sempre), si è purtroppo incapaci di liberare questa memoria e quindi si ha una perdita di memoria.

Tutte le altre risposte finora sono nella mia definizione, non in realtà perdite di memoria. Tutti mirano a riempire la memoria di cose inutili in fretta. Ma in qualsiasi momento puoi ancora dereferenziare gli oggetti che hai creato e liberare così la memoria -> NO LEAK. acconrad’s answer comes pretty close though as I have to admit since his solution is effectively to just “crash” the garbage collector by forcing it in an endless loop).

The long answer is: You can get a memory leak by writing a library for Java using the JNI, which can have manual memory management and thus have memory leaks. If you call this library, your java process will leak memory. Or, you can have bugs in the JVM, so that the JVM looses memory. There are probably bugs in the JVM, there may even be some known ones since garbage collection is not that trivial, but then it’s still a bug. By design this is not possible. You may be asking for some java code that is effected by such a bug. Sorry I don’t know one and it might well not be a bug anymore in the next Java version anyway.

Here’s a simple/sinister one via http://wiki.eclipse.org/Performance_Bloopers#String.substring.28.29 .

 public class StringLeaker { private final String muchSmallerString; public StringLeaker() { // Imagine the whole Declaration of Independence here String veryLongString = "We hold these truths to be self-evident..."; // The substring here maintains a reference to the internal char[] // representation of the original string. this.muchSmallerString = veryLongString.substring(0, 1); } } 

Because the substring refers to the internal representation of the original, much longer string, the original stays in memory. Thus, as long as you have a StringLeaker in play, you have the whole original string in memory, too, even though you might think you’re just holding on to a single-character string.

The way to avoid storing an unwanted reference to the original string is to do something like this:

 ... this.muchSmallerString = new String(veryLongString.substring(0, 1)); ... 

For added badness, you might also .intern() the substring:

 ... this.muchSmallerString = veryLongString.substring(0, 1).intern(); ... 

Doing so will keep both the original long string and the derived substring in memory even after the StringLeaker instance has been discarded.

A common example of this in GUI code is when creating a widget/component and adding a listener to some static/application scoped object and then not removing the listener when the widget is destroyed. Not only do you get a memory leak, but also a performance hit as when whatever you are listening to fires events, all your old listeners are called too.

Take any web application running in any servlet container (Tomcat, Jetty, Glassfish, whatever…). Redeploy the app 10 or 20 times in a row (it may be enough to simply touch the WAR in the server’s autodeploy directory.

Unless anybody has actually tested this, chances are high that you’ll get an OutOfMemoryError after a couple of redeployments, because the application did not take care to clean up after itself. You may even find a bug in your server with this test.

The problem is, the lifetime of the container is longer than the lifetime of your application. You have to make sure that all references the container might have to objects or classs of your application can be garbage collected.

If there is just one reference surviving the undeployment of your web app, the corresponding classloader and by consequence all classs of your web app cannot be garbage collected.

Threads started by your application, ThreadLocal variables, logging appenders are some of the usual suspects to cause classloader leaks.

Maybe by using external native code through JNI?

With pure Java, it is almost impossible.

But that is about a “standard” type of memory leak, when you cannot access the memory anymore, but it is still owned by the application. You can instead keep references to unused objects, or open streams without closing them afterwards.

I have had a nice “memory leak” in relation to PermGen and XML parsing once. The XML parser we used (I can’t remember which one it was) did a String.intern() on tag names, to make comparison faster. One of our customers had the great idea to store data values not in XML attributes or text, but as tagnames, so we had a document like:

  <1>bla <2>foo ...  

In fact, they did not use numbers but longer textual IDs (around 20 characters), which were unique and came in at a rate of 10-15 million a day. That makes 200 MB of rubbish a day, which is never needed again, and never GCed (since it is in PermGen). We had permgen set to 512 MB, so it took around two days for the out-of-memory exception (OOME) to arrive…

I recently encountered a memory leak situation caused in a way by log4j.

Log4j has this mechanism called Nested Diagnostic Context(NDC) which is an instrument to distinguish interleaved log output from different sources. The granularity at which NDC works is threads, so it distinguishes log outputs from different threads separately.

In order to store thread specific tags, log4j’s NDC class uses a Hashtable which is keyed by the Thread object itself (as opposed to say the thread id), and thus till the NDC tag stays in memory all the objects that hang off of the thread object also stay in memory. In our web application we use NDC to tag logoutputs with a request id to distinguish logs from a single request separately. The container that associates the NDC tag with a thread, also removes it while returning the response from a request. The problem occurred when during the course of processing a request, a child thread was spawned, something like the following code:

 pubclic class RequestProcessor { private static final Logger logger = Logger.getLogger(RequestProcessor.class); public void doSomething() { .... final List hugeList = new ArrayList(10000); new Thread() { public void run() { logger.info("Child thread spawned") for(String s:hugeList) { .... } } }.start(); } } 

So an NDC context was associated with inline thread that was spawned. The thread object that was the key for this NDC context, is the inline thread which has the hugeList object hanging off of it. Hence even after the thread finished doing what it was doing, the reference to the hugeList was kept alive by the NDC context Hastable, thus causing a memory leak.

I thought it was interesting that no one used the internal class examples. If you have an internal class; it inherently maintains a reference to the containing class. Of course it is not technically a memory leak because Java WILL eventually clean it up; but this can cause classs to hang around longer than anticipated.

 public class Example1 { public Example2 getNewExample2() { return this.new Example2(); } public class Example2 { public Example2() {} } } 

Now if you call Example1 and get an Example2 discarding Example1, you will inherently still have a link to an Example1 object.

 public class Referencer { public static Example2 GetAnExample2() { Example1 ex = new Example1(); return ex.getNewExample2(); } public static void main(String[] args) { Example2 ex = Referencer.GetAnExample2(); // As long as ex is reachable; Example1 will always remain in memory. } } 

I’ve also heard a rumor that if you have a variable that exists for longer than a specific amount of time; Java assumes that it will always exist and will actually never try to clean it up if cannot be reached in code anymore. But that is completely unverified.

What’s a memory leak:

  • It’s caused by a bug or bad design.
  • It’s a waste of memory.
  • It gets worse over time.
  • The garbage collector cannot clean it.

Typical example:

A cache of objects is a good starting point to mess things up.

 private static final Map myCache = new HashMap<>(); public void getInfo(String key) { // uses cache Info info = myCache.get(key); if (info != null) return info; // if it's not in cache, then fetch it from the database info = Database.fetch(key); if (info == null) return null; // and store it in the cache myCache.put(key, info); return info; } 

Your cache grows and grows. And pretty soon the entire database gets sucked into memory. A better design uses an LRUMap (Only keeps recently used objects in cache).

Sure, you can make things a lot more complicated:

  • using ThreadLocal constructions.
  • adding more complex reference trees .
  • or leaks caused by 3rd party libraries .

What often happens:

If this Info object has references to other objects, which again have references to other objects. In a way you could also consider this to be some kind of memory leak, (caused by bad design).

Create a static Map and keep adding hard references to it. Those will never be GC’d.

 public class Leaker { private static final Map CACHE = new HashMap(); // Keep adding until failure. public static void addToCache(String key, Object value) { Leaker.CACHE.put(key, value); } } 

You can create a moving memory leak by creating a new instance of a class in that class’s finalize method. Bonus points if the finalizer creates multiple instances. Here’s a simple program that leaks the entire heap in sometime between a few seconds and a few minutes depending on your heap size:

 class Leakee { public void check() { if (depth > 2) { Leaker.done(); } } private int depth; public Leakee(int d) { depth = d; } protected void finalize() { new Leakee(depth + 1).check(); new Leakee(depth + 1).check(); } } public class Leaker { private static boolean makeMore = true; public static void done() { makeMore = false; } public static void main(String[] args) throws InterruptedException { // make a bunch of them until the garbage collector gets active while (makeMore) { new Leakee(0).check(); } // sit back and watch the finalizers chew through memory while (true) { Thread.sleep(1000); System.out.println("memory=" + Runtime.getRuntime().freeMemory() + " / " + Runtime.getRuntime().totalMemory()); } } } 

Everyone always forgets the native code route. Here’s a simple formula for a leak:

  1. Declare native method.
  2. In native method, call malloc . Don’t call free .
  3. Call the native method.

Remember, memory allocations in native code come from the JVM heap.

I came across a more subtle kind of resource leak recently. We open resources via class loader’s getResourceAsStream and it happened that the input stream handles were not closed.

Uhm, you might say, what an idiot.

Well, what makes this interesting is: this way, you can leak heap memory of the underlying process, rather than from JVM’s heap.

All you need is a jar file with a file inside which will be referenced from Java code. The bigger the jar file, the quicker memory gets allocated.

You can easily create such a jar with the following class:

 import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class BigJarCreator { public static void main(String[] args) throws IOException { ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File("big.jar"))); zos.putNextEntry(new ZipEntry("resource.txt")); zos.write("not too much in here".getBytes()); zos.closeEntry(); zos.putNextEntry(new ZipEntry("largeFile.out")); for (int i=0 ; i<10000000 ; i++) { zos.write((int) (Math.round(Math.random()*100)+20)); } zos.closeEntry(); zos.close(); } } 

Just paste into a file named BigJarCreator.java, compile and run it from command line:

 javac BigJarCreator.java java -cp . BigJarCreator 

Et voilà: you find a jar archive in your current working directory with two files inside.

Let's create a second class:

 public class MemLeak { public static void main(String[] args) throws InterruptedException { int ITERATIONS=100000; for (int i=0 ; i 

This class basically does nothing, but create unreferenced InputStream objects. Those objects will be garbage collected immediately and thus, do not contribute to heap size. It is important for our example to load an existing resource from a jar file, and size does matter here!

If you're doubtful, try to compile and start the class above, but make sure to chose a decent heap size (2 MB):

 javac MemLeak.java java -Xmx2m -classpath .:big.jar MemLeak 

You will not encounter an OOM error here, as no references are kept, the application will keep running no matter how large you chose ITERATIONS in the above example. The memory consumption of your process (visible in top (RES/RSS) or process explorer) grows unless the application gets to the wait command. In the setup above, it will allocate around 150 MB in memory.

If you want the application to play safe, close the input stream right where it's created:

 MemLeak.class.getClassLoader().getResourceAsStream("resource.txt").close(); 

and your process will not exceed 35 MB, independent of the iteration count.

Quite simple and surprising.

The interviewer was probably looking for a circular reference like the code below (which incidentally only leak memory in very old JVMs that used reference counting, which isn’t the case any more). But it’s a pretty vague question, so it’s a prime opportunity to show off your understanding of JVM memory management.

 class A { B bRef; } class B { A aRef; } public class Main { public static void main(String args[]) { A myA = new A(); B myB = new B(); myA.bRef = myB; myB.aRef = myA; myA=null; myB=null; /* at this point, there is no access to the myA and myB objects, */ /* even though both objects still have active references. */ } /* main */ } 

Then you can explain that with reference counting, the above code would leak memory. But most modern JVMs don’t use reference counting any longer, most use a sweep garbage collector, which will in fact collect this memory.

Next you might explain creating an Object that has an underlying native resource, like this:

 public class Main { public static void main(String args[]) { Socket s = new Socket(InetAddress.getByName("google.com"),80); s=null; /* at this point, because you didn't close the socket properly, */ /* you have a leak of a native descriptor, which uses memory. */ } } 

Then you can explain this is technically a memory leak, but really the leak is caused by native code in the JVM allocating underlying native resources, which weren’t freed by your Java code.

At the end of the day, with a modern JVM, you need to write some Java code that allocates a native resource outside the normal scope of the JVM’s awareness.

I don’t think anyone has said this yet: you can resurrect an object by overriding the finalize() method such that finalize() stores a reference of this somewhere. The garbage collector will only be called once on the object so after that the object will never destroyed.

As a lot of people have suggested, Resource Leaks are fairly easy to cause – like the JDBC examples. Actual Memory leaks are a bit harder – especially if you aren’t relying on broken bits of the JVM to do it for you…

The ideas of creating objects that have a very large footprint and then not being able to access them aren’t real memory leaks either. If nothing can access it then it will be garbage collected, and if something can access it then it’s not a leak…

One way that used to work though – and I don’t know if it still does – is to have a three-deep circular chain. As in Object A has a reference to Object B, Object B has a reference to Object C and Object C has a reference to Object A. The GC was clever enough to know that a two deep chain – as in A <--> B – can safely be collected if A and B aren’t accessible by anything else, but couldn’t handle the three-way chain…

Threads are not collected until they terminate. They serve as roots of garbage collection. They are one of the few objects that won’t be reclaimed simply by forgetting about them or clearing references to them.

Consider: the basic pattern to terminate a worker thread is to set some condition variable seen by the thread. The thread can check the variable periodically and use that as a signal to terminate. If the variable is not declared volatile , then the change to the variable might not be seen by the thread, so it won’t know to terminate. Or imagine if some threads want to update a shared object, but deadlock while trying to lock on it.

If you only have a handful of threads these bugs will probably be obvious because your program will stop working properly. If you have a thread pool that creates more threads as needed, then the obsolete/stuck threads might not be noticed, and will accumulate indefinitely, causing a memory leak. Threads are likely to use other data in your application, so will also prevent anything they directly reference from ever being collected.

As a toy example:

 static void leakMe(final Object object) { new Thread() { public void run() { Object o = object; for (;;) { try { sleep(Long.MAX_VALUE); } catch (InterruptedException e) {} } } }.start(); } 

Call System.gc() all you like, but the object passed to leakMe will never die.

(*edited*)

there are many different situations memory will leak. One i encountered, which expose a map that should not be exposed and used in other place.

 public class ServiceFactory { private Map services; private static ServiceFactory singleton; private ServiceFactory() { services = new HashMap(); } public static synchronized ServiceFactory getDefault() { if (singleton == null) { singleton = new ServiceFactory(); } return singleton; } public void addService(String name, Service serv) { services.put(name, serv); } public void removeService(String name) { services.remove(name); } public Service getService(String name, Service serv) { return services.get(name); } // the problematic api, which expose the map. //and user can do quite a lot of thing from this api. //for example, create service reference and forget to dispose or set it null //in all this is a dangerous api, and should not expose public Map getAllServices() { return services; } } // resource class is a heavy class class Service { } 

I think that a valid example could be using ThreadLocal variables in an environment where threads are pooled.

For instance, using ThreadLocal variables in Servlets to communicate with other web components, having the threads being created by the container and maintaining the idle ones in a pool. ThreadLocal variables, if not correctly cleaned up, will live there until, possibly, the same web component overwrites their values.

Of course, once identified, the problem can be solved easily.

The interviewer might have be looking for a circular reference solution:

  public static void main(String[] args) { while (true) { Element first = new Element(); first.next = new Element(); first.next.next = first; } } 

This is a classic problem with reference counting garbage collectors. You would then politely explain that JVMs use a much more sophisticated algorithm that doesn’t have this limitation.

-Wes Tarle