Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do my variables not go out of scope?

Good afternoon all,

I was taught that when a function returns, The variables (within the scope of that function) automatically go out of scope so we do not have to set them to null.

However, this doesn't seem to be true.

I have a test code that creates a java.lang.ref.PhantomReference pointing to an instance of a java.lang.Object. The only strong reference to that object is within the scope of a function F.

In other words, when that function returns, there should no longer be any strong reference to that object, and the object should now be collectible by the the GC.

However, no matter how hard I try to starve the JVM of memory, the GC simply refuses to collect the object. What is surprising is that if I set the variable to null (obj = null;), the GC now collects the object.

What is the explanation behind this oddity?

public class Test {
    public static void main(String args[]) {
        // currently testing on a 64-bit HotSpot Server VM, but the other JVMs should probably have the same behavior for this use case
        Test test = new Test();
        test.F(new Object());
    }

    public <T> void F(T obj) {
        java.lang.ref.ReferenceQueue<T> ref_queue = new java.lang.ref.ReferenceQueue<T>();
        java.lang.ref.PhantomReference<T> ref = new java.lang.ref.PhantomReference<T>(obj, ref_queue); // if this line isn't an assignment, the GC wouldn't collect the object no matter how hard I force it to 
        obj = null; // if this line is removed, the GC wouldn't collect the object no matter how hard I force it to
        StartPollingRef(ref_queue);
        GoOom();
    }

    private <T> void StartPollingRef(final java.lang.ref.ReferenceQueue<T> ref_queue) {
        new java.lang.Thread(new java.lang.Runnable() {
            @Override
            public void run() {
                System.out.println("Removing..");
                boolean removed = false;
                while (!removed) {
                    try {
                        ref_queue.remove();
                        removed = true;
                        System.out.println("Removed.");
                    } catch (InterruptedException e) { // ignore
                    }
                }
            }
        }).start();
    }

    private void GoOom() {
        try {
            int len = (int) java.lang.Math.min(java.lang.Integer.MAX_VALUE, Runtime.getRuntime().maxMemory());
            Object[] arr = new Object[len];
        } catch (Throwable e) {
            // System.out.println(e);
        }
    }
}
like image 312
Pacerier Avatar asked Feb 25 '12 01:02

Pacerier


People also ask

Do static variables go out of scope?

A static variable has a property to retain its value from it's previous scope. This means that it's value does not get re-initialized if the function in which it is declared gets called multiple times.

How do you access local variables outside the scope?

Local variables cannot be accessed outside the function declaration. Global variable and local variable can have same name without affecting each other.

What happens when a variable of reference data type goes out of scope?

Nothing physical happens. A typical implementation will allocate enough space in the program stack to store all variables at the deepest level of block nesting in the current function. This space is typically allocated in the stack in one shot at the function startup and released back at the function exit.

What does it mean for a variable to go out of scope?

A variable declared within a block of code has local scope, and is only accessible by other code within the same block. Once the block within which it is declared is exited, the variable goes out of scope.


1 Answers

A standards-compliant JVM is never obligated to collect memory. That is to say, you cannot write a program whose correctness depends on a particular bit of memory being collected at a certain time: you can neither force the JVM to collect (even via System.gc()!) nor rely on it doing so.

So, the behavior you're observing cannot, definitionally, be wrong: you're purposefully trying to make the environment do something it is under no onus to do.

That all said, your issue is that your object has not gone out of scope. It is created in main, then passed - in the normal Java referential manner - to F. Until F returns, the T obj name is still a reference to your object.

Make goOom static and put a call to it in main, and you should see the object get collected. But, then again, you might still not, and that wouldn't be wrong...

like image 58
Borealid Avatar answered Oct 19 '22 14:10

Borealid