it's my test code
public class Test {
public static SoftReference<byte[]> cache = new SoftReference<>(new byte[0]);
public static List<byte[]> list = new ArrayList<>();
public static void main(String[] args) {
try {
func();
} catch (OutOfMemoryError e) {
sniff();
e.printStackTrace();
}
}
public static void func() {
byte[] bytes = new byte[1024 * 1024];
cache = new SoftReference<>(bytes);
for(;;) {
byte[] tmp = new byte[1024 * 1024];
list.add(tmp);
}
}
public static void sniff() {
byte[] bytes = cache.get();
if (bytes == null) {
System.out.println("recycling data.");
} else {
System.out.println("object still live");
}
}
}
The program output is as follows
object still live
I don't understand? why? This is a sentence I found in the official documentation of Oracle:
All soft references to softly-reachable objects are guaranteed to have been cleared before the virtual machine throws an OutOfMemoryError.
Even more strangely, if I put byte[] bytes = new byte[1024 * 1024]; cache = new SoftReference<>(bytes); into the for loop; like this
public class Test {
public static SoftReference<byte[]> cache = new SoftReference<>(new byte[0]);
public static List<byte[]> list = new ArrayList<>();
public static void main(String[] args) {
try {
func();
} catch (OutOfMemoryError e) {
sniff();
e.printStackTrace();
}
}
public static void func() {
for(;;) {
byte[] tmp = new byte[1024 * 1024];
list.add(tmp);
byte[] bytes = new byte[1024 * 1024];
cache = new SoftReference<>(bytes);
}
}
public static void sniff() {
byte[] bytes = cache.get();
if (bytes == null) {
System.out.println("recycling data.");
} else {
System.out.println("object still live");
}
}
}
the program output is as follows:
recycling data.
I have two question:
- The first writing method,Why did the garbage collector not collect SoftReferences
- Why do these two writing methods produce such a difference
I analyzed the dump file and the first way of writing really doesn't collect it
The important thing to note in the quote from the official documentation is in bold below:
Softly-reachable objects are those that can be reached by the "root" context only through soft references or weaker (reference).
In your first example you are putting your
byte[]in aList; this creates a path of strong references to the object - that is why it is not garbage collected in the end.In your second example, the array referenced by the
tmpvariable is added to the list and lives at least as long as the list lives. The array referenced by thebytesvariable however is created and is strongly referenced in the scope offunc()only. The only reference that escapes is the soft reference. So, before theOutOfMemoryErrorit is guaranteed that this softly reachable array, the last you created in the loop, will be garbage collected. The ones you created in the loop iterations before the last are already unreachable, because even the soft reference to them that survived the loop is overwritten in the next iteration.You could try changing the list to be
List<SoftReference<byte[]>>; you will find out that all of the soft references will have been cleared before the OOME.