Shenandoah Garbage Collector Load Reference Barriers - java

It is not a big secret for people who have watched the development of Shenandoah that a major criticism is that it employs GC barriers for every single write and read : be it reference or primitive.
Shenandoah 2.0 claims that this is not a problem anymore and it is solved via so-called load reference barriers. How is this exactly happening?

I will assume that the reader knows what a barriers is and why it is needed. For a very short intro here is another answer of mine on the topic.
In order to properly understand that, we need to first look at where the initial problem really was. Let's take a rather simple example:
static class User {
private int zip;
private int age;
}
static class Holder {
private User user;
// other fields we don't care about
}
And now let's image a theoretical method like this:
public void access(Holder holder){
User user = holder.user;
for(;;){ // some loop here
int zip = user.zip;
System.out.println(zip);
user.age = // some value taken from the loop for example
}
}
The idea is not to show a correct example, but an example that does:
a read (user.zip;)
a write (user.age = ...)
Now because Shenandoah 1.0 needed to introduce barriers everywhere, this code would look:
public void access(Holder holder){
User user = RB(holder).user;
for(;;){ // some loop here
int zip = RB(user).zip;
System.out.println(zip);
WB(user).age = // some value taken from the loop for example
}
}
Notice the RB(holder).user (RB stands for read barrier) and WB(user).age (WB stands for write barrier). Now imagine that the loop is hot - you will pay the price for so many barriers. Even if there is no GC activity during the execution of the loop, the barriers are still in place and there has to be code that conditionally checks if the barrier needs to be executed or not.
Long story short: those barriers are not free, by any means.
These barriers are needed to maintain heap consistency, because there are two copies of an Object in memory during evacuation phase, you need to always read and write consistently. Consistently here means that in Shenandoah 1.0 a read could have happened from the "to-space" or "from-space" (called "weak to-space invariant"), while a write could happen from to-space only.
Shenandoah 2.0 says that it will ensure a so-called "to-space invariant" (as opposed to the previous weak one). Basically - it says that all the writes and reads are going to happen from/into the "to-space". During evacuation there are two copies of the Object: one in the old region (called "from-space") and one in the new region (called "to-space").
It achieves this "to-space" invariant with a rather simple, yet brilliant idea. Instead of employing barriers where writes happen, it ensures that the Object that was initially loaded was for sure loaded from the "to-space". This is done via load-reference-barriers. This is far more trivial to understand via refactoring the previous example:
public void access(Holder holder){
User user = LRB(holder).user;
for(;;){ // some loop here
int zip = user.zip;
System.out.println(zip);
user.age = // some value taken from the loop for example
}
}
We have introduced the LRB barrier and removed two other. So, load-reference-barriers happen when an object is loaded, they call this : at the definition site, instead of when reading or storing to it, they call this at their use-site. You can think about it as if these barriers are inserted where aload and getField (for references) is used.

Related

In java in which thread is an object runned

I cannot understand why a thread is able to call a method of an object that is declered in an another thread which is in a while true loop.
From my basic knowledge if a thread is in a while true loop you cannot interact with it and therefore even with the object declered in this thread.
Thank you in advice.
this is the main class
/**
* main
*/
public class mainClass {
public static void main(String[] args) {
whatTime wt = new whatTime();
threadA ta = new threadA(wt);
ta.start();
while (true) {
}
}
}
this is the thread A class
/**
* threadA
*/
public class threadA extends Thread {
private whatTime wt;
public threadA(whatTime wt) {
System.out.println("threadA() constructor");
this.wt = wt;
}
public void run() {
while (true) {
//every 10s
try {
Thread.sleep(10000);
System.out.println("threadA: " + wt.getTime());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
and this is the object that i use
public class whatTime {
public whatTime() {
System.out.println("whatTime() constructor");
}
public long getTime() {
return System.currentTimeMillis();
}
}
I cannot understand why a thread is able to call a method of an object that is declered in an another thread
I think you misunderstand.
whatTime wt = new whatTime();
This does 2 completely different things.
In the local space (which is in the stack, and indeed, cannot be interacted with by other threads, whatsoever - that stack is instantly reused by whatever happens immediately after this method returns, so if any other thread dares to try, you're in big trouble) - it declares a reference variable. That's generally a 64-bit value that tells the JVM where to find an object in the heap. It is not the instance of whatTime itself that is stored in wt! - it is merely a reference to it. Think: "An page in an address book with the address of a house on it", not: "A house". an instanceof whatTime is the house, wt is the address book page.
Other threads cannot interact with the address book page - at best, you can make a copy of it, and hand the copy to another thread. But they can take their copy of the address book page, drive over there, toss a brick through the window. If later that day you also drive over there you will - see the damaged window.
new WhatTime() - that 'builds' the house, and assigns the address of that house to the wt variable.
threadA ta = new threadA(wt);
This makes a new instance of threadA (and also notes the adress of it onto the local variable space), and hands a copy of your address book page to it (java is pass-by-copy, always, but wt is the reference, not the object itself). You now have an address book page with '123 Fairfield lane' on it, and so does threadA - threadA has its own private copy. Whatever threadA does with that address book page has no effect on your address book. But if they decide to go to the house at that address - you can see that too.
From my basic knowledge if a thread is in a while true loop you cannot interact with it
Where you say 'in a while true loop', what you really mean is 'if it is running'. It doesn't matter if it is looping, it just matters that it is running.
What this is referring to is the memory model. Modern CPUs cannot read or write memory at all. Because that memory bus is waaaay too far away, those electrons are flying through the lines on your motherboard at ~60% the speed of light or similar, and that means it's like slow molasses relative to the CPU, that's why they cannot do that. Instead, there is a small chunk of very fast memory directly on the core, split up in a few 'pages' that have a set size (Something like 64k - depends on your processor), and the only thing a CPU can do is tell the memory controller: Go write this page back to main memory (all 64k of it) at this address, then wipe it out and replace it with the contents of the main memory banks from A to B. I'll wait. All 1000 cycles of it, because, boy, I need to wait a long time for you to do this stuff because that memory bank is so far away.
Given that that is how CPUs work, java needs to 'claim' some rights because if it didn't it would run incredibly slowly. One of the rights it claims is reordering and local caching.
A JVM may cache anything you get from the heap (so any non-local) in the on-die cache of the CPU core. Which means if you have 1 field, and 2 threads are simultaneously reading and writing to that field, each thread may actually just be writing to its local copy, and the JVM is non-specific as to how it will merge this back into memory - generally, some arbitrary page write 'wins' and it's all unreliable.
In other words, if 2 threads are accessing the same bit of heap (the same field), your code is broken: Depending on the CPU, architecture, music playing on your music player as you do the operation, or the phase of the moon - you get one result or another. Ouch.
To make threading possible in the first place, the JMM (Java Memory Model) defines certain specific operations that establish 'Happens-Before'. If according to the JMM action A 'happens before' action B, then the B line, and any line that follows it, will not be able to observe state such as it was before A ran. Essentially, If A 'Happens-Before (per the JMM)' B, then A really does happen before B, unless B couldn't possibly observe this, in which case the JVM is still free to run these things in whatever order it wants.
Establishing HB can be done with synchronized, volatile, various peculiar actions (thread.start() is HB relative to the first line inside that thread, for example), and of course by using (core) library functions that itself do this stuff, such as ConcurrentHashMap and many other things from the j.u.c package.
This is, presumably, what you read and misunderstood: You should not interact (read or write) any field that another thread that has started and hasn't died yet also interacts with, unless you have carefully managed this access and ensured that all possible interactions are probably guarded by HB/HA relationships so that your code will run the same way every time and didn't turn into some crazy evil coin flip game where it depends on the flaps of the proverbial butterfly. If you break the rule and you interact with the same field from 2 separate threads without establishing HB/HA anyway - then 'it works' (nothing directly crashes, no exceptions are thrown, the code compiles fine, and will run) - but the results are more or less arbitrary:
class Example {
int x;
void crazy() {
x = 1;
new Thread(() -> x = 5).start();
new Thread(() -> x = 10).start();
System.out.println(x);
}
}
The above code can legally print 1, 5, or 10. A JVM is working properly and is fulfilling the spec no matter which one of those it happens to return - and the JVM doesn't guarantee it's random either (a JVM that always returns 1 here, is fine. A JVM that always returns 1 except it returns 10 on the 5th monday of the 3rd month - is fine). Code written like this is the problem. There's no way to know, really. It's essentially impossible to test for it (given that a JVM that always returns 1 is also fine here). The vast majority of ways you can run this (combos of JVM vendor, version, OS, architecture, etc) will print '1', but a '10' wouldn't be incorrect.
Here in this specific code, there is no problem at all - wt the reference is written once, and this is HB relative to the thread (because it precedes t.start()), so accessing the ref isn't an issue as it never changes, and System.currentTimeMillis() doesn't access any fields, so the call to .getTime() isn't problematic either.
Hence, this code works.. just fine. There is nothing wrong with it.

java: If I assign a variable the same value as just before, does it change the memory or does JIT recognize this?

For example:
class Main {
public boolean hasBeenUpdated = false;
public void updateMain(){
this.hasBeenUpdated = true;
/*
alternative:
if(!hasBeenUpdated){
this.hasBeenUpdated = true;
}
*/
}
public void persistUpdate(){
this.hasBeenUpdated = false;
}
}
public Main instance = new Main()
instance.updateMain()
instance.updateMain()
instance.updateMain()
Does instance.hasBeenUpdated get updated 3 times in memory?
The reason I ask this is because I hoped to use a boolean("hasBeenUpdated") as a flag, and this could theoretically be "changed" many, many times, before I call "instance.persistUpdate()".
Does the JVM's JIT see this and perform an optimization?
JIT will collapse redundant statements only when it can PROVE that removing the code will not change the behavior. For example, if you did this:
int i;
i = 1;
i = 1;
i = 1;
The first two assignments are provably redundant, and the JIT could eliminate them. If instead it's
int i;
i = someMethodReturningInt();
i = someMethodReturningInt();
i = someMethodReturningInt();
the JIT has no way of knowing what someMethodReturnintInt() does, including whether it has any side effects, so it must invoke the method 3 times. Whether or not it actually stores any but the final value is immaterial, as the code would behave the same either way. (Declaring volatile int i; instead would force it to store each value)
Of course if you're doing other things in between the method invocations the it will be forced to perform the assignment.
The whole topic is part of the more general "happens-before" and "happens-after" concepts documented in the language and JVM specifications.
Optimization is NEVER supposed to change the behavior of a program, except possibly to reduce its runtime. There have been instances where bugs in the optimizer inadvertently did introduce errors, but these have been few and far between. In general you don't need to worry about whether optimization will break your code.
It can perform an optimization, yes.
As a matter of fact, it can issue a single write, or a single call to updateMain. All those three calls will be collapsed to one, only.
But for that to happen, JIT has to prove that nothing else breaks, or more specifically that code does not break the JMM rules. In this specific case, as far as I understand it, it does not.
Given the choice is between JVM code that implements
move new value to variable
and
compare new value with current value of variable
if not the same
move new value to variable
the JVM would have to be fairly nutty to implement it the latter way. That's a pessimization, not an optimization.
The JVM to a large extent relies on the real machine to do simple operations, and real machines store values in memory when you tell them to store values in memory.

How is LongAccumulator implemented, so that it is more efficient?

I understand that the new Java (8) has introduced new sychronization tools such as LongAccumulator (under the atomic package).
In the documentation it says that the LongAccumulator is more efficient when the variable update from several threads is frequent.
I wonder how is it implemented to be more efficient?
That's a very good question, because it shows a very important characteristic of concurrent programming with shared memory. Before going into details, I have to make a step back. Take a look at the following class:
class Accumulator {
private final AtomicLong value = new AtomicLong(0);
public void accumulate(long value) {
this.value.addAndGet(value);
}
public long get() {
return this.value.get();
}
}
If you create one instance of this class and invoke the method accumulate(1) from one thread in a loop, then the execution will be really fast. However, if you invoke the method on the same instance from two threads, the execution will be about two magnitudes slower.
You have to take a look at the memory architecture to understand what happens. Most systems nowadays have a non-uniform memory access. In particular, each core has its own L1 cache, which is typically structured into cache lines with 64 octets. If a core executes an atomic increment operation on a memory location, it first has to get exclusive access to the corresponding cache line. That's expensive, if it has no exclusive access yet, due to the required coordination with all other cores.
There's a simple and counter-intuitive trick to solve this problem. Take a look at the following class:
class Accumulator {
private final AtomicLong[] values = {
new AtomicLong(0),
new AtomicLong(0),
new AtomicLong(0),
new AtomicLong(0),
};
public void accumulate(long value) {
int index = getMagicValue();
this.values[index % values.length].addAndGet(value);
}
public long get() {
long result = 0;
for (AtomicLong value : values) {
result += value.get();
}
return result;
}
}
At first glance, this class seems to be more expensive due to the additional operations. However, it might be several times faster than the first class, because it has a higher probability, that the executing core already has exclusive access to the required cache line.
To make this really fast, you have to consider a few more things:
The different atomic counters should be located on different cache lines. Otherwise you replace one problem with another, namely false sharing. In Java you can use a long[8 * 4] for that purpose, and only use the indexes 0, 8, 16 and 24.
The number of counters have to be chosen wisely. If there are too few different counters, there are still too many cache switches. if there are too many counters, you waste space in the L1 caches.
The method getMagicValue should return a value with an affinity to the core id.
To sum up, LongAccumulator is more efficient for some use cases, because it uses redundant memory for frequently used write operations, in order to reduce the number of times, that cache lines have to be exchange between cores. On the other hand, read operations are slightly more expensive, because they have to create a consistent result.
by this
http://codenav.org/code.html?project=/jdk/1.8.0-ea&path=/Source%20Packages/java.util.concurrent.atomic/LongAccumulator.java
it looks like a spin lock.

Is memory leaks possible in java? [duplicate]

I just had an interview where I was asked to create a memory leak with Java.
Needless to say, I felt pretty dumb, having no idea how to start creating one.
What would an example be?
Here's a good way to create a true memory leak (objects inaccessible by running code but still stored in memory) in pure Java:
The application creates a long-running thread (or use a thread pool to leak even faster).
The thread loads a class via an (optionally custom) ClassLoader.
The class allocates a large chunk of memory (e.g. new byte[1000000]), stores a strong reference to it in a static field, and then stores a reference to itself in a ThreadLocal. Allocating the extra memory is optional (leaking the class instance is enough), but it will make the leak work that much faster.
The application clears all references to the custom class or the ClassLoader it was loaded from.
Repeat.
Due to the way ThreadLocal is implemented in Oracle's JDK, this creates a memory leak:
Each Thread has a private field threadLocals, which actually stores the thread-local values.
Each key in this map is a weak reference to a ThreadLocal object, so after that ThreadLocal object is garbage-collected, its entry is removed from the map.
But each value is a strong reference, so when a value (directly or indirectly) points to the ThreadLocal object that is its key, that object will neither be garbage-collected nor removed from the map as long as the thread lives.
In this example, the chain of strong references looks like this:
Thread object → threadLocals map → instance of example class → example class → static ThreadLocal field → ThreadLocal object.
(The ClassLoader doesn't really play a role in creating the leak, it just makes the leak worse because of this additional reference chain: example class → ClassLoader → all the classes it has loaded. It was even worse in many JVM implementations, especially prior to Java 7, because classes and ClassLoaders were allocated straight into permgen and were never garbage-collected at all.)
A variation on this pattern is why application containers (like Tomcat) can leak memory like a sieve if you frequently redeploy applications which happen to use ThreadLocals that in some way point back to themselves. This can happen for a number of subtle reasons and is often hard to debug and/or fix.
Update: Since lots of people keep asking for it, here's some example code that shows this behavior in action.
Static field holding an object reference [especially a final field]
class MemorableClass {
static final ArrayList list = new ArrayList(100);
}
(Unclosed) open streams (file , network, etc.)
try {
BufferedReader br = new BufferedReader(new FileReader(inputFile));
...
...
} catch (Exception e) {
e.printStackTrace();
}
Unclosed connections
try {
Connection conn = ConnectionFactory.getConnection();
...
...
} catch (Exception e) {
e.printStackTrace();
}
Areas that are unreachable from JVM's garbage collector, such as memory allocated through native methods.
In web applications, some objects are stored in application scope until the application is explicitly stopped or removed.
getServletContext().setAttribute("SOME_MAP", map);
Incorrect or inappropriate JVM options, such as the noclassgc option on IBM JDK that prevents unused class garbage collection
See IBM JDK settings.
A simple thing to do is to use a HashSet with an incorrect (or non-existent) hashCode() or equals(), and then keep adding "duplicates". Instead of ignoring duplicates as it should, the set will only ever grow and you won't be able to remove them.
If you want these bad keys/elements to hang around you can use a static field like
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.
Below there will be a non-obvious case where Java leaks, besides the standard case of forgotten listeners, static references, bogus/modifiable keys in hashmaps, or just threads stuck without any chance to end their life-cycle.
File.deleteOnExit() - always leaks the string, if the string is a substring, the leak is even worse (the underlying char[] is also leaked) - in Java 7 substring also copies the char[], so the later doesn't apply; #Daniel, no needs for votes, though.
I'll concentrate on threads to show the danger of unmanaged threads mostly, don't wish to even touch swing.
Runtime.addShutdownHook and not remove... and then even with removeShutdownHook due to a bug in ThreadGroup class regarding unstarted threads it may not get collected, effectively leak the ThreadGroup. JGroup has the leak in GossipRouter.
Creating, but not starting, a Thread goes into the same category as above.
Creating a thread inherits the ContextClassLoader and AccessControlContext, plus the ThreadGroup and any InheritedThreadLocal, all those references are potential leaks, along with the entire classes loaded by the classloader and all static references, and ja-ja. The effect is especially visible with the entire j.u.c.Executor framework that features a super simple ThreadFactory interface, yet most developers have no clue of the lurking danger. Also a lot of libraries do start threads upon request (way too many industry popular libraries).
ThreadLocal caches; those are evil in many cases. I am sure everyone has seen quite a bit of simple caches based on ThreadLocal, well the bad news: if the thread keeps going more than expected the life the context ClassLoader, it is a pure nice little leak. Do not use ThreadLocal caches unless really needed.
Calling ThreadGroup.destroy() when the ThreadGroup has no threads itself, but it still keeps child ThreadGroups. A bad leak that will prevent the ThreadGroup to remove from its parent, but all the children become un-enumerateable.
Using WeakHashMap and the value (in)directly references the key. This is a hard one to find without a heap dump. That applies to all extended Weak/SoftReference that might keep a hard reference back to the guarded object.
Using java.net.URL with the HTTP(S) protocol and loading the resource from(!). This one is special, the KeepAliveCache creates a new thread in the system ThreadGroup which leaks the current thread's context classloader. The thread is created upon the first request when no alive thread exists, so either you may get lucky or just leak. The leak is already fixed in Java 7 and the code that creates thread properly removes the context classloader. There are few more cases (like ImageFetcher, also fixed) of creating similar threads.
Using InflaterInputStream passing new java.util.zip.Inflater() in the constructor (PNGImageDecoder for instance) and not calling end() of the inflater. Well, if you pass in the constructor with just new, no chance... And yes, calling close() on the stream does not close the inflater if it's manually passed as constructor parameter. This is not a true leak since it'd be released by the finalizer... when it deems it necessary. Till that moment it eats native memory so badly it can cause Linux oom_killer to kill the process with impunity. The main issue is that finalization in Java is very unreliable and G1 made it worse till 7.0.2. Moral of the story: release native resources as soon as you can; the finalizer is just too poor.
The same case with java.util.zip.Deflater. This one is far worse since Deflater is memory hungry in Java, i.e. always uses 15 bits (max) and 8 memory levels (9 is max) allocating several hundreds KB of native memory. Fortunately, Deflater is not widely used and to my knowledge JDK contains no misuses. Always call end() if you manually create a Deflater or Inflater. The best part of the last two: you can't find them via normal profiling tools available.
(I can add some more time wasters I have encountered upon request.)
Good luck and stay safe; leaks are evil!
Most examples here are "too complex". They are edge cases. With these examples, the programmer made a mistake (like don't redefining equals/hashcode), or has been bitten by a corner case of the JVM/JAVA (load of class with static...). I think that's not the type of example an interviewer want or even the most common case.
But there are really simpler cases for memory leaks. The garbage collector only frees what is no longer referenced. We as Java developers don't care about memory. We allocate it when needed and let it be freed automatically. Fine.
But any long-lived application tend to have shared state. It can be anything, statics, singletons... Often non-trivial applications tend to make complex objects graphs. Just forgetting to set a reference to null or more often forgetting to remove one object from a collection is enough to make a memory leak.
Of course all sort of listeners (like UI listeners), caches, or any long-lived shared state tend to produce memory leak if not properly handled. What shall be understood is that this is not a Java corner case, or a problem with the garbage collector. It is a design problem. We design that we add a listener to a long-lived object, but we don't remove the listener when no longer needed. We cache objects, but we have no strategy to remove them from the cache.
We maybe have a complex graph that store the previous state that is needed by a computation. But the previous state is itself linked to the state before and so on.
Like we have to close SQL connections or files. We need to set proper references to null and remove elements from the collection. We shall have proper caching strategies (maximum memory size, number of elements, or timers). All objects that allow a listener to be notified must provide both a addListener and removeListener method. And when these notifiers are no longer used, they must clear their listener list.
A memory leak is indeed truly possible and is perfectly predictable. No need for special language features or corner cases. Memory leaks are either an indicator that something is maybe missing or even of design problems.
The answer depends entirely on what the interviewer thought they were asking.
Is it possible in practice to make Java leak? Of course it is, and there are plenty of examples in the other answers.
But there are multiple meta-questions that may have been being asked?
Is a theoretically "perfect" Java implementation vulnerable to leaks?
Does the candidate understand the difference between theory and reality?
Does the candidate understand how garbage collection works?
Or how garbage collection is supposed to work in an ideal case?
Do they know they can call other languages through native interfaces?
Do they know to leak memory in those other languages?
Does the candidate even know what memory management is, and what is going on behind the scene in Java?
I'm reading your meta-question as "What's an answer I could have used in this interview situation". And hence, I'm going to focus on interview skills instead of Java. I believe you're more likely to repeat the situation of not knowing the answer to a question in an interview than you are to be in a place of needing to know how to make Java leak. So, hopefully, this will help.
One of the most important skills you can develop for interviewing is learning to actively listen to the questions and working with the interviewer to extract their intent. Not only does this let you answer their question the way they want, but also shows that you have some vital communication skills. And when it comes down to a choice between many equally talented developers, I'll hire the one who listens, thinks, and understands before they respond every time.
The following is a pretty pointless example if you do not understand JDBC. Or at least how JDBC expects a developer to close Connection, Statement, and ResultSet instances before discarding them or losing references to them, instead of relying on implementing the finalize method.
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);
}
}
The problem with the above is that the Connection object is not closed, and hence the physical Connection will remain open until the garbage collector comes around and sees that it is unreachable. GC will invoke the finalize method, but there are JDBC drivers that do not implement the finalize, at least not in the same way that Connection.close is implemented. The resulting behavior is that while the JVM will reclaim memory due to unreachable objects being collected, resources (including memory) associated with the Connection object might not be reclaimed.
As such, Connection's final method does not clean up everything. One might find that the physical Connection to the database server will last several garbage collection cycles until the database server eventually figures out that the Connection is not alive (if it does) and should be closed.
Even if the JDBC driver implemented finalize, the compiler can throw exceptions during finalization. The resulting behavior is that any memory associated with the now "dormant" object will not be reclaimed by the compiler, as finalize is guaranteed to be invoked only once.
The above scenario of encountering exceptions during object finalization is related to another scenario that could lead to a memory leak - object resurrection. Object resurrection is often done intentionally by creating a strong reference to the object from being finalized, from another object. When object resurrection is misused it will lead to a memory leak in combination with other sources of memory leaks.
There are plenty more examples that you can conjure up - like
Managing a List instance where you are only adding to the list and not deleting from it (although you should be getting rid of elements you no longer need), or
Opening Sockets or Files, but not closing them when they are no longer needed (similar to the above example involving the Connection class).
Not unloading Singletons when bringing down a Java EE application. The Classloader that loaded the singleton class will retain a reference to the class, and hence the singleton instance will never be collected by the JVM. When a new instance of the application is deployed, a new class loader is usually created, and the former class loader will continue to exist due to the singleton.
Probably one of the simplest examples of a potential memory leak, and how to avoid it, is the implementation of 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;
}
If you were implementing it yourself, would you have thought to clear the array element that is no longer used (elementData[--size] = null)? That reference might keep a huge object alive ...
Any time you keep references around to objects that you no longer need you have a memory leak. See Handling memory leaks in Java programs for examples of how memory leaks manifest themselves in Java and what you can do about it.
You are able to make memory leak with sun.misc.Unsafe class. In fact this service class is used in different standard classes (for example in java.nio classes). You can't create instances of this class directly, but you may use reflection to get an instance.
Code doesn't compile in the Eclipse IDE - compile it using command javac (during compilation you'll get warnings)
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();
}
}
}
I can copy my answer from here:
Easiest way to cause memory leak in Java
"A memory leak, in computer science (or leakage, in this context), occurs when a computer program consumes memory but is unable to release it back to the operating system." (Wikipedia)
The easy answer is: You can't. Java does automatic memory management and will free resources that are not needed for you. You can't stop this from happening. It will always be able to release the resources. In programs with manual memory management, this is different. You can get some memory in C using malloc(). To free the memory, you need the pointer that malloc returned and call free() on it. But if you don't have the pointer any more (overwritten, or lifetime exceeded), then you are unfortunately incapable of freeing this memory and thus you have a memory leak.
All the other answers so far are in my definition not really memory leaks. They all aim at filling the memory with pointless stuff real fast. But at any time you could still dereference the objects you created and thus freeing the memory --> 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 any more 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 application 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 classes of your application can be garbage collected.
If there is just one reference surviving the undeployment of your web application, the corresponding classloader and by consequence all classes of your web application 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:
<data>
<1>bla</1>
<2>foo</>
...
</data>
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...
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 anymore). 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.
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<String, Info> 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).
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 classes 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.
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<String> hugeList = new ArrayList<String>(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.
Create a static Map and keep adding hard references to it. Those will never be garbage collected.
public class Leaker {
private static final Map<String, Object> CACHE = new HashMap<String, Object>();
// Keep adding until failure.
public static void addToCache(String key, Object value) { Leaker.CACHE.put(key, value); }
}
Everyone always forgets the native code route. Here's a simple formula for a leak:
Declare a native method.
In the native method, call malloc. Don't call free.
Call the native method.
Remember, memory allocations in native code come from the JVM heap.
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());
}
}
}
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.
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<ITERATIONS ; i++) {
MemLeak.class.getClassLoader().getResourceAsStream("resource.txt");
}
System.out.println("finished creation of streams, now waiting to be killed");
Thread.sleep(Long.MAX_VALUE);
}
}
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.
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...
Another way to create potentially huge memory leaks is to hold references to Map.Entry<K,V> of a TreeMap.
It is hard to asses why this applies only to TreeMaps, but by looking at the implementation the reason might be that: a TreeMap.Entry stores references to its siblings, therefore if a TreeMap is ready to be collected, but some other class holds a reference to any of its Map.Entry, then the entire Map will be retained into memory.
Real-life scenario:
Imagine having a db query that returns a big TreeMap data structure. People usually use TreeMaps as the element insertion order is retained.
public static Map<String, Integer> pseudoQueryDatabase();
If the query was called lots of times and, for each query (so, for each Map returned) you save an Entry somewhere, the memory would constantly keep growing.
Consider the following wrapper class:
class EntryHolder {
Map.Entry<String, Integer> entry;
EntryHolder(Map.Entry<String, Integer> entry) {
this.entry = entry;
}
}
Application:
public class LeakTest {
private final List<EntryHolder> holdersCache = new ArrayList<>();
private static final int MAP_SIZE = 100_000;
public void run() {
// create 500 entries each holding a reference to an Entry of a TreeMap
IntStream.range(0, 500).forEach(value -> {
// create map
final Map<String, Integer> map = pseudoQueryDatabase();
final int index = new Random().nextInt(MAP_SIZE);
// get random entry from map
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue().equals(index)) {
holdersCache.add(new EntryHolder(entry));
break;
}
}
// to observe behavior in visualvm
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
public static Map<String, Integer> pseudoQueryDatabase() {
final Map<String, Integer> map = new TreeMap<>();
IntStream.range(0, MAP_SIZE).forEach(i -> map.put(String.valueOf(i), i));
return map;
}
public static void main(String[] args) throws Exception {
new LeakTest().run();
}
}
After each pseudoQueryDatabase() call, the map instances should be ready for collection, but it won't happen, as at least one Entry is stored somewhere else.
Depending on your jvm settings, the application may crash in the early stage due to a OutOfMemoryError.
You can see from this visualvm graph how the memory keeps growing.
The same does not happen with a hashed data-structure (HashMap).
This is the graph when using a HashMap.
The solution? Just directly save the key / value (as you probably already do) rather than saving the Map.Entry.
I have written a more extensive benchmark here.
There are many good examples of memory leaks in Java, and I will mention two of them in this answer.
Example 1:
Here is a good example of a memory leak from the book Effective Java, Third Edition (item 7: Eliminate obsolete object references):
// Can you spot the "memory leak"?
public class Stack {
private static final int DEFAULT_INITIAL_CAPACITY = 16;
private Object[] elements;
private int size = 0;
public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size == 0) throw new EmptyStackException();
return elements[--size];
}
/*** Ensure space for at least one more element, roughly* doubling the capacity each time the array needs to grow.*/
private void ensureCapacity() {
if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
This is the paragraph of the book that describes why this implementation will cause a memory leak:
If a stack grows and then shrinks, the objects that were popped off the
stack will not be garbage collected, even if the program using the
stack has no more references to them. This is because the
stack maintains obsolete references to these objects. An obsolete
reference is simply a reference that will never be dereferenced
again. In this case, any references outside of the “active portion” of
the element array are obsolete. The active portion consists of the
elements whose index is less than size
Here is the solution of the book to tackle this memory leak:
The fix for this sort of problem is simple: null out
references once they become obsolete. In the case of our Stack class,
the reference to an item becomes obsolete as soon as it’s popped
off the stack. The corrected version of the pop method looks like this:
public Object pop() {
if (size == 0) throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}
But how can we prevent a memory leak from happening? This is a good caveat from the book:
Generally speaking, whenever a class manages its own memory,
the programmer should be alert for memory leaks. Whenever an element
is freed, any object references contained in the element should be
nulled out.
Example 2:
The observer pattern also can cause a memory leak. You can read about this pattern in the following link: Observer pattern.
This is one implementation of the Observer pattern:
class EventSource {
public interface Observer {
void update(String event);
}
private final List<Observer> observers = new ArrayList<>();
private void notifyObservers(String event) {
observers.forEach(observer -> observer.update(event)); //alternative lambda expression: observers.forEach(Observer::update);
}
public void addObserver(Observer observer) {
observers.add(observer);
}
public void scanSystemIn() {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
notifyObservers(line);
}
}
}
In this implementation, EventSource, which is Observable in the Observer design pattern, can hold links to Observer objects, but this link is never removed from the observers field in EventSource. So they will never be collected by the garbage collector. One solution to tackle this problem is providing another method to the client for removing the aforementioned observers from the observers field when they don't need those observers anymore:
public void removeObserver(Observer observer) {
observers.remove(observer);
}
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.
The interviewer might have been 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.

Gracefully finalizing the SoftReference referent

I am using a search library which advises keeping search handle object open for this can benefit query cache. Over the time I have observed that the cache tends to get bloated (few hundred megs and keeps growing) and OOMs started to kick in. There is no way to enforce limits of this cache nor plan how much memory it can use. So I have increased the Xmx limit, but that's only a temporary solution to the problem.
Eventually I am thinking to make this object a referent of java.lang.ref.SoftReference. So if the system runs low on free memory, it would let the object go and a new one would be created on demand. This would decrease some speed after fresh start, but this is a much better alternative than hitting OOM.
The only problem I see about SoftReferences is that there is no clean way of getting their referents finalized. In my case, before destroying the search handle I need to close it, otherwise the system might run out of file descriptors. Obviously, I can wrap this handle into another object, write a finalizer on it (or hook onto a ReferenceQueue/PhantomReference) and let go. But hey, every single article in this planet advises against using finalizers, and especially - against finalizers for freeing file handles (e.g. Effective Java ed. II, page 27.).
So I am somewhat puzzled. Should I carefully ignore all these advices and go on. Otherwise, are there any other viable alternatives? Thanks in advance.
EDIT #1: Text below was added after testing some code as suggested by Tom Hawtin. To me, it appears that either suggestion isn't working or I am missing something. Here's the code:
class Bloat { // just a heap filler really
private double a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;
private final int ii;
public Bloat(final int ii) {
this.ii = ii;
}
}
// as recommended by Tom Hawtin
class MyReference<T> extends SoftReference<T> {
private final T hardRef;
MyReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
this.hardRef = referent;
}
}
//...meanwhile, somewhere in the neighbouring galaxy...
{
ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
int i=0;
while(i<50000) {
// set.add(new MyReference<Bloat>(new Bloat(i), rq));
set.add(new SoftReference<Bloat>(new Bloat(i), rq));
// MyReference<Bloat> polled = (MyReference<Bloat>) rq.poll();
SoftReference<Bloat> polled = (SoftReference<Bloat>) rq.poll();
if (polled != null) {
Bloat polledBloat = polled.get();
if (polledBloat == null) {
System.out.println("is null :(");
} else {
System.out.println("is not null!");
}
}
i++;
}
}
If I run the snippet above with -Xmx10m and SoftReferences (as in code above), I'm getting tons of is null :( printed. But if I replace the code with MyReference (uncommenting two lines with MyReference and commenting out ones with SoftReference) I always get OOM.
As I understood from the advice, having hard reference inside MyReference should not prevent object hitting ReferenceQueue, right?
For a finite number of resources: Subclass SoftReference. The soft reference should point to the enclosing object. A strong reference in the subclass should reference the resource, so it is always strongly reachable. When read through the ReferenceQueue poll the resource can be closed and removed from the cache. The cache needs to be released correctly (if a SoftReference itself is garbage collected, it can't be enqueued onto a ReferenceQueue).
Be careful that you only have a finite number of resources unreleased in the cache - evict old entries (indeed, you can discard the soft references with if finite cache, if that suits your situation). It is usually the case that it is the non-memory resource which is more important, in which case an LRU-eviction cache with no exotic reference objects should be sufficient.
(My answer #1000. Posted from London DevDay.)
Toms answer is the correct one, however the code that has been added to the question is not the same as what was proposed by Tom. What Tom was proposing looks more like this:
class Bloat { // just a heap filler really
public Reader res;
private double a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;
private final int ii;
public Bloat(final int ii, Reader res) {
this.ii = ii;
this.res = res;
}
}
// as recommended by Tom Hawtin
class MySoftBloatReference extends SoftReference<Bloat> {
public final Reader hardRef;
MySoftBloatReference(Bloat referent, ReferenceQueue<Bloat> q) {
super(referent, q);
this.hardRef = referent.res;
}
}
//...meanwhile, somewhere in the neighbouring galaxy...
{
ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
int i=0;
while(i<50000) {
set.add(new MySoftBloatReference(new Bloat(i, new StringReader("test")), rq));
MySoftBloatReference polled = (MySoftBloatReference) rq.poll();
if (polled != null) {
// close the reference that we are holding on to
try {
polled.hardRef.close();
} catch (IOException e) {
e.printStackTrace();
}
}
i++;
}
}
Note that the big difference is that the hard reference is to the object that needs to be closed. The surrounding object can, and will, be garbage collected, so you won't hit the OOM, however you still get a chance to close the reference. Once you leave the loop, that will also be garbage collected. Of course, in the real world, you probably wouldn't make res a public instance member.
That said, if you are holding open file references, then you run a very real risk of running out of those before you run out of memory. You probably also want to have an LRU cache to ensure that you keep no more than sticks finger in the air 500 open files. These can also be of type MyReference so that they can also be garbage collected if need be.
To clarify a little on how MySoftBloatReference works, the base class, that is SoftReference, still holds the reference to the object that is hogging all of the memory. This is the object that you need to be freed to prevent the OOM from happening. However, If the object is freed, you still need to free the resources that the Bloat is using, that is, Bloat is using two types of resource, memory and a file handle, both of these resources need to be freed, or you run out of one or the other of the resources. The SoftReference handles the pressure on the memory resource by freeing that object, however you also need to release the other resource, the file handle. Because Bloat has already been freed, we can't use it to free the related resource, so MySoftBloatReference keeps a hard reference to the internal resource that needs to be closed. Once it has been informed that the Bloat has been freed, i.e. once the reference turns up in the ReferenceQueue, then MySoftBloatReference can also close the related resource, through the hard reference that it has.
EDIT: Updated the code so that it compiles when thrown into a class. It uses a StringReader to illustrate the concept of how to close the Reader, which is being used to represent the external resource that needs to be freed. In this particular case closing that stream is effectively a no-op, and so is not needed, but it shows how to do so if it is needed.
Ahm.
(As far as I know) You can't hold the stick from both ends. Either you hold to your information, or you let it go.
However... you can hold to some key information that would enable you to finalize. Of course, the key information must be significantly smaller then the "real information" and must not have the real information in its reachable object graph (weak references might help you there).
Building on the existing example (pay attention to the key information field):
public class Test1 {
static class Bloat { // just a heap filler really
private double a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;
private final int ii;
public Bloat(final int ii) {
this.ii = ii;
}
}
// as recommended by Tom Hawtin
static class MyReference<T, K> extends SoftReference<T> {
private final K keyInformation;
MyReference(T referent, K keyInformation, ReferenceQueue<? super T> q) {
super(referent, q);
this.keyInformation = keyInformation;
}
public K getKeyInformation() {
return keyInformation;
}
}
//...meanwhile, somewhere in the neighbouring galaxy...
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
int i = 0;
while (i < 50000) {
set.add(new MyReference<Bloat, Integer>(new Bloat(i), i, rq));
final Reference<? extends Bloat> polled = rq.poll();
if (polled != null) {
if (polled instanceof MyReference) {
final Object keyInfo = ((MyReference) polled).getKeyInformation();
System.out.println("not null, got key info: " + keyInfo + ", finalizing...");
} else {
System.out.println("null, can't finalize.");
}
rq.remove();
System.out.println("removed reference");
}
Edit:
I want to elaborate on the "either hold your information or let it go". Assuming you had some way of holding to your information. That would have forced the GC to unmark your data, causing the data to actually be cleaned only after you're done with it, in a second GC cycle. This is possible - and its exactly what finalize() is for. Since you stated that you don't want the second cycle to occur, you can't hold your information (if a-->b then !b-->!a). which means you must let it go.
Edit2:
Actually, a second cycle would occur - but for your "key data", not your "major bloat data". The actual data would be cleared on the first cycle.
Edit3:
Obviously, the real solution would use a separate thread for removing from the reference queue (don't poll(), remove(), blocking on the dedicated thread).
#Paul - thanks a lot for the answer and clarification.
#Ran - I think in your current code i++ is missing at the end of the loop. Also, you don't need to do rq.remove() in the loop as rq.poll() already removes top reference, isn't it?
Few points:
1) I had to add Thread.sleep(1) statement after i++ in the loop (for both solutions of Paul and Ran) to avoid OOM but that's irrelevant to the big picture and is also platform dependant. My machine has a quad-core CPU and is running Sun Linux 1.6.0_16 JDK.
2) After looking at these solutions I think I'll stick using finalizers. Bloch's book provides following reasons:
there is no guarantee finalizers will be executed promptly, therefore never do anything time critical in a finalizer -- nor there are any guarantees for SoftRererences!
Never depend on a finalizer to update critical persistent state -- I am not
there is a severe performance penalty for using finalizers -- In my worst case I'd be finalizing about a single object per minute or so. I think I can live with that.
use try/finally -- oh yes, I definitely will!
Having necessity to create enormous amount of scaffold just for what seems a simple task doesn't look reasonable to me.
I mean, literally, the WTF per minute rate would be quite high for anybody else looking at such code.
3) Saddly, there is no way to split points between Paul, Tom and Ran :(
I hope Tom wouldn't mind as he already got lots of them :) Judging between Paul and Ran was much harder - I think both answers work and are correct. I am only setting accept flag to Paul's answer because it was rated higher (and has more detailed explanation), but Ran's solution isn't bad at all and would probably be my choice if I'd chose to implement it using SoftReferences. Thanks guys!

Categories

Resources