Java: setting a reference to null won't affect

2020-07-14 09:12发布

问题:

I've got a simple question. In this code below, why is s3's value still printed although I set it to null before. It seems gargbage collector won't get called.

public class Test {
    public static void main(String[] args) {
         String s1 = "abc", s2 = "def", s3 = "ghj";

         String sarr[] = {s1, s2, s3};
         s3 = null;

         System.gc(); 

         for(int i = 0; i < sarr.length; i++) {
             System.out.print(sarr[i] + " "); //prints abc def ghj
         }
    }
 }

Any thoughts would be appreciated.

回答1:

When you write:

// Moved [] to make it more idiomatic
String[] sarr = {s1, s2, s3};

That copies the values of s1, s2 and s3 (which are references, not objects) into the array. When the statement has executed, the variables are entirely independent of the array. Changing the value of any of the s1, s2, s3 variables to refer to a different string doesn't affect the contents of the array at all.

It's exactly the same as with other assignments, e.g.

string x = "hello";
string y = x;
x = null; // Doesn't affect y

and method arguments, too.

Note that the garbage collection part is a red herring here - while obviously understanding this is important to understanding garbage collection, the reverse isn't true. The important points are to understand that:

  • The values of s1, s2, and s3 and the elements of the array are references, not objects.
  • The values are copied when creating the array, so changing the variable afterwards doesn't make any difference

Now, to take the example a bit further, consider:

StringBuilder sb = new StringBuilder("Hello");
StringBuilder[] array = { sb };

sb.append(" world");
System.out.println(array[0]);

This will print "Hello world" because here we've only got a single StringBuilder object, which both sb and array[0] refer to. Changing the data inside that object (which isn't the same as changing sb to refer to a different object) makes the change visible however you get at it. This is like me giving the address of my house to two people - if one of them paints my house red, the other person will see that as well.



回答2:

You've reassigned s3 to point to something else (null), but the reference inside the array is still pointing to the original value ("ghj").

s3 is not a constant pointer to the array location. The value of s3 (the reference to the string value "ghj") was copied into the array. The variable itself is not connected and is free to change to reference another String object independently of what you do with the array contents.



回答3:

Understand that you cannot force the garbage collector to run; you can only suggest that it runs.

To answer your question, you need to remember the difference between reference and value. When you instantiate s3, you point it to "ghj". When you instantiate sarr[2], you point it to "ghj", too. You then null out the s3 reference, but sarr[2] is still pointing to the "ghj" string in the heap.



回答4:

I'm guessing that the string "ghj" is added to the string pool. When you set s3 = null, the s3 variable no longer references the ghj in the string pool but your array element sarr[2] does, hence the reason it still prints correctly.



回答5:

A Java String is immutable. Once you've made it, it won't change. What you've done is create a String (implicitly, through the "ghj" literal in your code) and assigned a reference to that string to a variable s3.

After that, you're placing your three variables into an array. What basically happens there is that the three references are given a place in an array.

Then, you set variable s3 to point to null. The reference that was stored in s3 is removed from it, replaced by a null pointer. But, that reference is still in the array. The reference to the String got copied into the array, not the actual String nor the variable itself.

So when you're printing out the array contents, a reference to your "ghj" String is still in there and will be used. Since this reference is also still actively reachable, it won't be garbage-collected.

Also mind that calling System.gc() is an indication that you want the garbage collector to run. You're not at all guaranteed that this will have happened when the method returns or within any time limit, for that matter.



回答6:

Because the s3 value is still referred to from the array.

What is also worth noting, is that there's never a guarantee that System.gc() will actually call the garbage collector. It's merely a suggestion to the JVM.