2
public class Currency {
    String name = new String();
    static Integer value = new Integer();
    static void getCurrency(Integer v) {
        Currency c = new Currency();
        c.value = v;
    }
    public static void m() {
        Currency.getCurrency(50);
        Currency.getCurrency(100);
    }
    public static void main(String[] argv) {
        Currency.m();
    }
}

I count 5 elements eligible for garbage collector when m() exits. The exercise says 6 is the correct answer. Can you help me?

user1365914
  • 858
  • 2
  • 18
  • 32
  • Can you tell us which 5 variables you thought were eligible for garbage collection? – Tim Biegeleisen Jul 12 '16 at 01:46
  • Two "Currency" objects created in getCurrency, for each of these objects a "name" (nonstatic) String is created on heap. Then I thought that c.value would just change the content of static Integer "value", but looks like I am wrong on this last part? – user1365914 Jul 12 '16 at 01:52
  • 1
    It's a trick question: No objects are eligible for GC, because no objects are created, given that `new Integer()` causes `The constructor Integer() is undefined` compilation error, and hence prevents the code from running. – Andreas Jul 12 '16 at 02:07
  • Counting objects eligible for GC is a fool's errand, because in nearly every modern JVM you also have a JIT compiler which can do escape analysis and dead code detection. A good JIT should identify all the allocations in this program as dead stores that never escape `m()` and avoid allocating any objects at all, so there is no garbage to collect. – Daniel Pryden Jul 12 '16 at 05:08
  • @Andreas you're right but the exercise doesn't have a choice "Compiler error", anyway :) – user1365914 Jul 12 '16 at 08:48

2 Answers2

1

The two calls made to getCurrency() each will create two Currency objects in total. Each call requires:

  • an Integer object as parameter, later assigned to value
  • a Currency object
  • a name string object (not static, i.e. each Currency object has one)

3 × 2 = 6

Update:

The big question seems to be as to whether a new Integer object will be created for each call to the Currency constructor. Autoboxing a primitive int into an Integer will call Integer.valueOf(). And the Javadoc has this to say about Integer.valueOf():

Returns an Integer instance representing the specified int value. If a new Integer instance is not required, this method should generally be used in preference to the constructor Integer(int), as this method is likely to yield significantly better space and time performance by caching frequently requested values. This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.

So values in the range -128 to 127 get cached. But the catch is that in your code, you are working with two different values, 50 and 100, so caching won't avoid creation of a new Integer object AFAIK.

Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
  • 1
    Each call made to `getCurrency()` will create *one* Currency object. The call to `m()` will therefore create *two* Currency objects. – Andreas Jul 12 '16 at 01:38
  • But shouldn't the `Integer` values passed in in this example be part of the `Integer` cache, and so not eligible for garbage collection? – resueman Jul 12 '16 at 01:38
  • @Andreas Sorry I answered this on a cell phone. – Tim Biegeleisen Jul 12 '16 at 01:40
  • 1
    @Vwin Yes. I don't see how that affects what I'm talking about though. – resueman Jul 12 '16 at 01:40
  • @resueman You are correct, but `value` is initialized to `new Integer()`, so when replaced by `50`, that object is GC-able. When the 50 Integer object is replaced by the 100 Integer object, the 50 is GC-able. After `m()`, the 100 is still reachable. – Andreas Jul 12 '16 at 01:41
  • @Andreas but wouldn't 50 still be references by the cache, and so not eligible for collection? – resueman Jul 12 '16 at 01:42
  • @resueman There is only *one* static `value`, so it can only "cache" the *last* value, i.e. `100`. – Andreas Jul 12 '16 at 01:42
  • @TimBiegeleisen I'm not "blogging" your answer; I'm asking a question about it. I don't understand how the `Integer` created by `50` is eligible for garbage collection, since my understanding is that boxed values, at least in the range -128 to 127, are automatically cached by the `Integer` class, and so would still be referenced. – resueman Jul 12 '16 at 01:46
  • 1
    We are all **wrong**. No objects are eligible for GC, because no objects are created, given that `new Integer()` causes `The constructor Integer() is undefined` compilation error, and hence prevents the code from running. – Andreas Jul 12 '16 at 02:07
  • @Andreas you're right but the exercise doesn't have a choice "Compiler error", anyway :) – user1365914 Jul 12 '16 at 08:50
0

I disagree with the accepted answer. I believe your answer of five objects being eligible for garbage collection is actually correct, and the exercise you are working from is wrong (Although if anyone can explain how I'm mistaken, I'd greatly appreciate it).

Here are the objects that are referenced by your program:

  1. Integer #0 - The Integer created by static Integer value = new Integer()*
  2. Currency #1 - The Currency created by Currency.getCurrency(50).
  3. Integer #1 - The Integer returned by the autoboxing of 50.
  4. String #1 - The String created by String name = new String() in Currency #1's creation.
  5. Currency #2 - The Currency created by Currency.getCurrency(100).
  6. Integer #2 - The Integer returned by the autoboxing of 100.
  7. String #2 - The String created by String name = new String() in Currency #2's creation.

*this doesn't actually compile, but I'll assume it's replaced by valid code which doesn't otherwise affect the program.


So seven objects are required for your code to function. The object initially referenced by value becomes eligible for garbage collection when value is overwritten with 50. The first and second Currencys are eligible at the end of m when their only references go out of scope. The first and second Strings are eligible at the end of m, when their only referencing objects, Currencys 1 and 2, go out of scope. The autoboxed Integer from 100 is clearly still referenced at the end by the static reference value, and so is not eligible for garbage collection. That's 5 objects eligible, 1 ineligible, and one not yet discussed.

The last object is the Integer returned by the autoboxing of 50. Your code obviously no longer references it, since value is set to reference 100 instead. This would seem to indicate that it's eligible for garbage collection, but I disagree. The Integer class is know to cache values at least in the range -128 to 127 in order to conform to JLS 5.1.7. So when m exits, the cache will still contain a reference to the boxed Integer from 50, causing it to be ineligible for garbage collection.


I attempted to test this behavior with this program:

public class Test{
    public static final WeakReference<Integer> inCache = new WeakReference<>(50);
    public static final WeakReference<Integer> outOfCache = new WeakReference<>(10000);
    public static void main(String[] args){
        System.gc();
        System.out.println(inCache.get());
        System.out.println(outOfCache.get());
    }
}

Which outputs

50
null

showing that, at least in this case, the uncached Integer was garbage collected and the cached one was not. This suggests that even with no explicit reference to 50, it is still ineligible for garbage collection because of the stored reference in the cache.

I'll admit that this doesn't prove anything since, for one, System.gc() does not guarantee that it will collect all (or any) available memory. However, I think this should show that the problem isn't as straightforward as one might assume and, on the most common JDK/JVMs, it's plausible that the sixth value is not eligible for garbage collection.


TL;DR - I don't think the Integer returned by boxing 50 is eligible for garbage collection, because it is still referenced by the cache maintained by the Integer class.

Community
  • 1
  • 1
resueman
  • 10,572
  • 10
  • 31
  • 45
  • You are entirely correct, except that it is likely the wrong answer for the exercise. I say that because my estimation is that the entire concept of caching boxed `Integer` objects is beyond the theoretical knowledge of the people doing the exercise. If you ignore the internal optimization of boxed `Integer` objects, the answer is 6. If you account for the caching, the answer is 5. If you account for the compilation error in the question, the answer is 0 or N/A, whichever you prefer. – Andreas Jul 12 '16 at 04:02
  • All that is highlighted by the misrepresentation in your answer. You say *"The Integer created by the autoboxing of 50/100"*, which would indicate that the `Integer` object is created there. In reality, the `Integer` cache is pre-created, and the implicit `Integer.valueOf()` that autoboxing does will simply return the *already existing* cached object. So if your argument is that caching means the answer is 5, then at least describe the reason why correctly. – Andreas Jul 12 '16 at 04:08
  • @Andreas I don't feel that the creation time of the `Integer` is relevant to my point. A cache which lazily creates values as needed (when the implicit `Integer.valueOf` is called in this case) will still contain the cached reference at the end. I didn't say what I meant, but my error has no impact on the point of the answer. – resueman Jul 12 '16 at 04:24
  • @Andreas For the compilation error, I don't feel that's a fair complaint. The question of how many objects are eligible for garbage collection when the program finishes running becomes meaningless if the program is never able to run in the first place. The question only has meaning if you assume it compiles, with or without a cache. And the question asker may have been looking for the answer six, but that doesn't make them right and more than me writing an answer makes me right. There's still an objective truth, regardless of what anyone says about it. – resueman Jul 12 '16 at 04:24
  • If you ask your students a question, knowing that you haven't taught them everything they need to know to come up with the true answer (5), then you'd expect them to come up with the logical answer (6) that is within their knowledge-base (brain). Given the level of the student the "right" answer would be 6, but a student that is ahead of the curve should get an A+ for answering 5, assuming the student can accurately explain why 5 is more correct than 6. Anyway, that was my point, not saying that your answer isn't the *true* answer, just that 6 can also be considered a "correct" answer. – Andreas Jul 12 '16 at 04:48
  • Thank you for your correct and deep analysis, resueman. It helped me understanding more about the exercise. – user1365914 Jul 12 '16 at 08:55