Asked  7 Months ago    Answers:  5   Viewed   24 times

I've been looking into a bug in my code that seems to be caused by some "ugly" finalizer code. The code looks roughly like this

public class A {
   public B b = new B();
   @Override public void finalize() {
     b.close();
   }
}

public class B {
   public void close() { /* do clean up our resources. */ }
   public void doSomething() { /* do something that requires us not to be closed */ } 
}

void main() {
   A a = new A();
   B b = a.b;
   for(/*lots of time*/) {
     b.doSomething();
   }
}

What I think is happening is that a is getting detected as having no references after the second line of main() and getting GC'd and finalized by the finalizer thread - while the for loop is still happening, using b while a is still "in scope".

Is this plausable? Is java allowed to GC an object before it goes out of scope?

Note: I know that doing anything inside finalizers is bad. This is code I've inherited and am intending to fix - the question is whether I'm understanding the root issue correctly. If this is impossible then something more subtle must be the root of my bug.

 Answers

29

Can Java finalize an object when it is still in scope?

Yes.

However, I'm being pedantic here. Scope is a language concept that determines the validity of names. Whether an object can be garbage collected (and therefore finalized) depends on whether it is reachable.

The answer from ajb almost had it (+1) by citing a significant passage from the JLS. However I don't think it's directly applicable to the situation. JLS §12.6.1 also says:

A reachable object is any object that can be accessed in any potential continuing computation from any live thread.

Now consider this applied to the following code:

class A {
    @Override protected void finalize() {
        System.out.println(this + " was finalized!");
    }

    public static void main(String[] args) {
        A a = new A();
        System.out.println("Created " + a);
        for (int i = 0; i < 1_000_000_000; i++) {
            if (i % 1_000_000 == 0)
                System.gc();
        }
        // System.out.println(a + " was still alive.");
    }
}

On JDK 8 GA, this will finalize a every single time. If you uncomment the println at the end, a will never be finalized.

With the println commented out, one can see how the reachability rule applies. When the code reaches the loop, there is no possible way that the thread can have any access to a. Thus it is unreachable and is therefore subject to finalization and garbage collection.

Note that the name a is still in scope because one can use a anywhere within the enclosing block -- in this case the main method body -- from its declaration to the end of the block. The exact scope rules are covered in JLS §6.3. But really, as you can see, scope has nothing to do with reachability or garbage collection.

To prevent the object from being garbage collected, you can store a reference to it in a static field, or if you don't want to do that, you can keep it reachable by using it later on in the same method after the time-consuming loop. It should be sufficient to call an innocuous method like toString on it.

Tuesday, June 1, 2021
 
MannfromReno
answered 7 Months ago
37

GarbageCollectorMBean.getCollectionTime() return cumulative wall clock time in milliseconds for specific algorithm.

For Stop-the-World algorithms (young collections usually) this could be interpreted as time application was paused.

For concurrent algorithms that value is perty useless, because it is not STW time and it is not CPU time (algorithm may use multiple threads, but wall clock time would be calculated).

Correct CPU time consumed by GC algorithm could be retrieved from performance counters (here is example of code reading these counters).

Thursday, August 5, 2021
 
Mountains
answered 4 Months ago
28

why is this so?

Because early browsers did that, and it's now become standardized.

why do we have to even use getElementById(id) instead of simply writing id?

Technically, you don't. But beware that the global namespace is really, really crowded. There's a whole bunch of stuff thrown in there. Not just elements with IDs, but certain elements if they have names, the browser context by name, etc., etc., which means there can be conflicts. For instance, if you had an element with id="document", the automatic global won't be created. The other, conflicting globals can vary by browser. Also, id values that aren't valid JavaScript identifiers (like id="foo-bar") are still perfectly valid id values, but the automatic global for it (window["foo-bar"]) is awkward to use.

Using getElementById specifically looks for an element with that ID1 (not name, etc.). So it's more contained and reliable.


1 Ignoring bugs in obsolete versions of IE, which failed to constrain it correctly.

Thursday, August 26, 2021
 
Hugo
answered 4 Months ago
11

The last two lines do not help.

  • Once the list variable goes out of scope*, if that's the last reference to the linked list then the list becomes eligible for garbage collection. Setting list to null immediately beforehand adds no value.

  • Once the list becomes eligible for garbage collection, so to do its elements if the list holds the only references to them. Clearing the list is unnecessary.

For the most part you can trust the garbage collector to do its job and do not need to "help" it.

* Pedantically speaking, it's not scope that controls garbage collection, but reachability. Reachability isn't easy to sum up in one sentence. See this Q&A for an explanation of this distinction.


One common exception to this rule is if you have code that will retain references longer than they're needed. The canonical example of this is with listeners. If you add a listener to some component, and later on that listener is no longer needed, you need to explicitly remove it. If you don't, that listener can inhibit garbage collection of both itself and of the objects it has references to.

Let's say I added a listener to a button like so:

button.addListener(event -> label.setText("clicked!"));

Then later on the label is removed, but the button remains.

window.removeChild(label);

This is a problem because the button has a reference to the listener and the listener has a reference to the label. The label can't be garbage collected even though it's no longer visible on screen.

This is a time to take action and get on the GC's good side. I need to remember the listener when I add it...

Listener listener = event -> label.setText("clicked!");
button.addListener(listener);

...so that I can remove it when I'm done with the label:

window.removeChild(label);
button.removeListener(listener);
Saturday, October 16, 2021
 
Willy
answered 2 Months ago
54

...and that's somewhat the point of it. Weak references are useful, if you don't want to (or cannot afford to) retain an object indefinetly in memory. Consider the following use case: you need to associate information with classes. Now, since you are running in an environment, where classes might get reloaded (say, a Tomcat, or OSGi environment), you want the garbage collector to be able to reclaim old versions of a class as soon as it deems safe to do so.

An initial attempt to implement this, might look like

class ClassAssoc {
    private final IdentityHashMap<Class<?>,MyMetaData> cache = new ...;
}

The problem here is: this would keep all classes in the cache member forever (or at least, unless they are manually removed), forcing the garbage collector to retain them indefinitly, including everything referenced from the class (static member values, class loader information, ...)

By using weak references, the garbage collector can reclaim old version of the class as soon as no other references to it (usually instances) exist. On the other hand: as long as such references exist, the value is guaranteed to be also reachable from the weak reference object, and thus, is a valid key in the cache table.

Add concurrency and other atrocities to the picture, and you are at what MapMaker optionally also provides...

Thursday, November 11, 2021
 
Laimoncijus
answered 4 Weeks ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share