A DESCRIPTION OF THE REQUEST :
JVM have to analyse liveness of reference type local variables precisely in order to prevent unintentional reference retention.
JUSTIFICATION :
Local variables (which values became unused at some moment) continue hold reference to objects. This may lead to unnecessary OutOfMemoryError since GC is not able to collect such objects.
There are some examples:
public class Block extends Base {
public static void main(String[] args) {
{
Object enduring = newHugeObject();
}
// 'enduring' should not retain 'huge object' anymore
{
newHugeObject(); // Oops!
}
}
}
public class For extends Base {
public static void main(String[] args) {
Object enduring = null;
for(int i = 0; i < 2; ++i) {
useObject(enduring);
// current 'enduring' value will not be read,
// so the local variable should not retain 'huge object' anymore
enduring = newHugeObject(); // Oops!
}
useObject(enduring);
}
}
public class IfElse extends Base {
public static void main(String[] args) {
Object enduring = newHugeObject();
if(alwaysTrue()) {
// 'enduring' is not used in then-branch and later,
// so it should not retain 'huge object' in spite of
// else-branch uses it
newHugeObject(); // Oops!
}
else {
useObject(enduring);
}
}
}
public class IfThen extends Base {
public static void main(String[] args) {
Object enduring = newHugeObject();
if(!alwaysTrue()) {
useObject(enduring);
}
else {
// 'enduring' is not used in else-branch and later,
// so it should not retain 'huge object' in spite of
// then-branch uses it
newHugeObject(); // Oops!
}
}
}
public class LastAccess extends Base {
public static void main(String[] args) {
Object enduring = newHugeObject();
useObject(enduring);
// it was the last access to current 'enduring' value,
// 'enduring' should not retain 'huge object' anymore
newHugeObject(); // Oops!
enduring = ""; // just rewrite
useObject(enduring);
}
}
public class Invoke extends Base {
public static void main(String[] args) {
Object enduring = newHugeObject();
// invokestatic #2; //Method newHugeObject:()Ljava/lang/Object;
// astore_1
// aload_1
// ; just after pushing onto stack,
// ; 'enduring' should not retain 'huge object'
// invokestatic #3; //Method method:(Ljava/lang/Object;)V
// return
method(enduring);
}
private static void method(Object huge) {
huge = null; // clear reference
newHugeObject(); // Oops!
}
}
// where Base contain utility methods
public class Base {
private static final int HUGE_SIZE;
static {
// more then half available memory
HUGE_SIZE = (int) Runtime.getRuntime().maxMemory() / 2 + 1;
}
protected static Object newHugeObject() {
return new byte[HUGE_SIZE];
}
protected static boolean alwaysTrue() {
return true;
}
protected static void useObject(Object o) {
// do nothing
}
}
CUSTOMER SUBMITTED WORKAROUND :
In some cases (except Invoke) it is possible to clear references manually:
enduring = null;
JVM have to analyse liveness of reference type local variables precisely in order to prevent unintentional reference retention.
JUSTIFICATION :
Local variables (which values became unused at some moment) continue hold reference to objects. This may lead to unnecessary OutOfMemoryError since GC is not able to collect such objects.
There are some examples:
public class Block extends Base {
public static void main(String[] args) {
{
Object enduring = newHugeObject();
}
// 'enduring' should not retain 'huge object' anymore
{
newHugeObject(); // Oops!
}
}
}
public class For extends Base {
public static void main(String[] args) {
Object enduring = null;
for(int i = 0; i < 2; ++i) {
useObject(enduring);
// current 'enduring' value will not be read,
// so the local variable should not retain 'huge object' anymore
enduring = newHugeObject(); // Oops!
}
useObject(enduring);
}
}
public class IfElse extends Base {
public static void main(String[] args) {
Object enduring = newHugeObject();
if(alwaysTrue()) {
// 'enduring' is not used in then-branch and later,
// so it should not retain 'huge object' in spite of
// else-branch uses it
newHugeObject(); // Oops!
}
else {
useObject(enduring);
}
}
}
public class IfThen extends Base {
public static void main(String[] args) {
Object enduring = newHugeObject();
if(!alwaysTrue()) {
useObject(enduring);
}
else {
// 'enduring' is not used in else-branch and later,
// so it should not retain 'huge object' in spite of
// then-branch uses it
newHugeObject(); // Oops!
}
}
}
public class LastAccess extends Base {
public static void main(String[] args) {
Object enduring = newHugeObject();
useObject(enduring);
// it was the last access to current 'enduring' value,
// 'enduring' should not retain 'huge object' anymore
newHugeObject(); // Oops!
enduring = ""; // just rewrite
useObject(enduring);
}
}
public class Invoke extends Base {
public static void main(String[] args) {
Object enduring = newHugeObject();
// invokestatic #2; //Method newHugeObject:()Ljava/lang/Object;
// astore_1
// aload_1
// ; just after pushing onto stack,
// ; 'enduring' should not retain 'huge object'
// invokestatic #3; //Method method:(Ljava/lang/Object;)V
// return
method(enduring);
}
private static void method(Object huge) {
huge = null; // clear reference
newHugeObject(); // Oops!
}
}
// where Base contain utility methods
public class Base {
private static final int HUGE_SIZE;
static {
// more then half available memory
HUGE_SIZE = (int) Runtime.getRuntime().maxMemory() / 2 + 1;
}
protected static Object newHugeObject() {
return new byte[HUGE_SIZE];
}
protected static boolean alwaysTrue() {
return true;
}
protected static void useObject(Object o) {
// do nothing
}
}
CUSTOMER SUBMITTED WORKAROUND :
In some cases (except Invoke) it is possible to clear references manually:
enduring = null;