20

In Java, I've done things like the following without thinking much about it:

public class Main {

    public void run() {
        // ...
    }

    public static void main(String[] args) {
        new Main().run();
    }
}

However, recently I've become unsure as to whether doing that is safe. After all, there is no reference to the Main object after it's created (well, there is the this reference, but does that count?), so it looks like there's a danger that the garbage collector might delete the object while it's in the middle of executing something. So perhaps the main method should look like this:

    public static void main(String[] args) {
        Main m = new Main();
        m.run();
    }

Now, I'm pretty sure that the first version works and I've never had any problems with it, but I'd like to know if it's safe to do in all cases (not only in a specific JVM, but preferably according to what the language specification says about such cases).

Socob
  • 1,189
  • 1
  • 12
  • 26
  • 1
    For the record, the equivalent construction in the Microsoft CLR *can* be collected while the method is still executing. See [When does an object become eligible for garbage collection?](http://blogs.msdn.com/b/oldnewthing/archive/2010/08/10/10048149.aspx) – Daniel Pryden Jan 01 '13 at 18:12
  • 2
    Possible duplicate: [Does the object which is the entry point of a Java program get garbage collected?](http://stackoverflow.com/questions/8699755/does-the-object-which-is-the-entry-point-of-a-java-program-get-garbage-collected?) – Daniel Pryden Jan 01 '13 at 18:23
  • 1
    @DanielPryden - Perhaps both questions are asking the same thing but this question is asked with much more clarity and has yielded more useful answers than the referenced question. – dj18 Jan 01 '13 at 19:57
  • 1
    @dj18: Agreed -- and that's why I didn't vote to close. But I felt that the cross reference was a useful addition, so I added a comment. – Daniel Pryden Jan 01 '13 at 20:06

4 Answers4

33

If an object method is being executed, it means someone is in possession of that reference. So no, an object can't be GC'd while a method is being executed.

duffymo
  • 305,152
  • 44
  • 369
  • 561
  • +1 The objects which are retained are those accessible from thread roots so they cannot be GCed by definition. – Peter Lawrey Jan 01 '13 at 15:31
  • 1
    Hm, I suspected something like this, but the problem I have is that there is no explicit reference (i.e. a variable) pointing to the created object at any point. Does that mean that, as far as the GC is concerned, as long as the object is used in a statement (such as calling a method on the object with the `.` operator), that counts as implicitly holding a reference to the object? – Socob Jan 01 '13 at 15:40
  • 5
    You still have the this reference. That counts as a normal reference. – Tesseract Jan 01 '13 at 16:12
  • A quote from the JLS would get you a +1 (if I can find one I'll add it later). – OrangeDog Jan 02 '13 at 00:08
  • Please do. I'm not interested in looking. – duffymo Jan 02 '13 at 01:01
  • 1
    I think this answer is incorrect. I gave my example code to prove it wrong in my answer below. Correct me if I'm wrong. – J Freebird Jun 05 '21 at 04:17
  • The object is reachable from the thread that is executing it. A thread is a GC root and can never be collected before it exits. – user207421 Jun 05 '21 at 04:26
3

For the most part garbage collection is transparent. It's there to remove the unnecessary complication of manual memory management. So, it will appear not to be collected, but what actually happens is more subtle.

Trivially, a compiler may completely elide the construction of the object. (By compiler, I mean a lower level compiler than javac. The bytecodes will be a literal transliteration of the source.) More obscurely, garbage collection typically runs in separate threads and actually remove the unaccessed object as a method on it is being run.

How can this be observed? The usual suspect in a finaliser. It may run concurrently with a method running on the object. Typically you would get around this problem with synchronized blocks in both the finaliser and the normal methods, which introduces the necessary happens-before relationship.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
2

m is just a variable which has reference stored. This will be used by programmer to use the same object further to write logic on same object.

While execution, program will be converted to OP-CODES / INSTRUCTIONS . These INSTRUCTION will have the reference to object(it is a memory location after all). In case m is present, location of object will be accessed via INDIRECT REFERENCE. If m is absent, the reference is DIRECT.

So here, object is being used by CPU registers, irrespective of use of reference variable.

This will be available till the flow of execution is in scope of main() function.

Further, as per GC process, GC only removes objects from memory, once GC is sure that the object will not be used any further.

Every object is given chance to survive a number of times(depends on situation and algorithm). Once the number of chances are over, then only object is garbage collected.

In simpler words, objects which were used recently, will be given chance to stay in memory. Old objects will be removed from memory.

So given your code:

public class Main {

public void run() {
    // ...
}

public static void main(String[] args) {
    new Main().run();
}
}

the object will not be garbage collected.

Also, for examples, try to look at anonymous class examples. Or examples from event handling in AWT / SWING.

There, you will find a lot of usage like this.

Learn More
  • 1,535
  • 4
  • 29
  • 51
1

The accepted answer is not correct. Whether the object can be GCed or not depends on if your public void run() {// ...} method has a reference to the class instance (this). Try:

public class FinalizeThis {

    private String a = "a";

    protected void finalize() {
        System.out.println("finalized!");
    }

    void loop() {
        System.out.println("loop() called");
        for (int i = 0; i < 1_000_000_000; i++) {
            if (i % 1_000_000 == 0)
                System.gc();
        }
        // System.out.println(a);
        System.out.println("loop() returns");
    }

    public static void main(String[] args) {
        new FinalizeThis().loop();
    }
}

The above program always outputs

loop() called
finalized!
loop() returns

in Java 8. If you, however, uncomment System.out.println(a), the output changes to

loop() called
a
loop() returns

There is no GC this time because the method called references the instance variable (this.a).

You can take look at this answer

J Freebird
  • 3,664
  • 7
  • 46
  • 81