"abc" 这样的 java 字符串文字可以被垃圾收集吗?。如果是,我们如何以编程方式证明它们是 GCed?

最佳答案

是的,在 Java7 之后,如果加载它的类加载器被垃圾收集并且没有对字符串文字的引用,则字符串文字可以被垃圾收集。

注意:在 Java -8 中,您必须调用 GC 两次以确保 ClassLoader 被 GC(Metaspace.. pff..Using a different GC 无济于事)。

Case -1 :
//ClassLoaders don't get GCed.

Code :
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;

// main class
 class TestStringLiteralGC {
    public static void main(String[] args) throws Exception {
        Class<?> c1 = new CustomClassLoader().loadClass("Test"); // load class once
        Class<?> c2 = new CustomClassLoader().loadClass("Test");  // load class again
        System.out.println("c1 : " + c1); // c1 : class Test
        System.out.println("c2 : " + c2); // c2 : class Test
        System.out.println("c1 == c2 :" + (c1 == c2)); //c1 == c2 :false --> So, now we have 2 different class objects for same class.
        Field f1 = c1.getDeclaredField("s"); // getting field s of c1
        f1.setAccessible(true);
        System.out.println("Identity hashCode of c1.s :"+ System.identityHashCode(f1.get(null))); // Identity hashCode of c1.s :1442407170
        Field f2 = c2.getDeclaredField("s");  // getting field s of c2
        f2.setAccessible(true);
        System.out.println("Identity hashCode of c2.s :"+ System.identityHashCode(f2.get(null))); // Identity hashCode of c2.s :1442407170
        System.out.println("c1.s == c2.s : " + (f1.get(null) == f2.get(null))); // c1.s == c2.s : true ==> c1.s is the same "instance" as c2.s
        //Don't make c1 and c2 eligible for GC
        // So, now, there are still references to "abc"
//      f1 = null;
//      c1 = null;
//      f2 = null;
//      c2 = null;
       //call GC explicitly. Yes, twice.
        Thread.sleep(1000);
        System.gc();
        Thread.sleep(1000);
        System.gc();
        Thread.sleep(1000);
        // use the same string literal in main. Just to test that the same literal is being used.
        String s = "abc";
        System.out.println("Identity hashCode of mainMethod's s : " + System.identityHashCode(s)); // Identity hashCode of mainMethod's s : 1442407170 ==> Yes. The IDHashcodes are the same
    }

}
// Our class which will be loaded
class Test {
    static String s = "abc"; // Our little hero!.The string literal.
}
//Our custom ClassLoader to load the class "Test"
class CustomClassLoader extends ClassLoader {
    // finalize() is to check if Object is unreachable (and ready for GC)
    protected void finalize() throws Throwable {
        System.out.println("CustomClassLoader finalize called.." + this);
    };

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if (!name.equals("Test")) {
            return super.loadClass(name);
        }
        try {
            InputStream in = ClassLoader
                    .getSystemResourceAsStream("Test.class");
            byte[] a = new byte[10000];
            int len = in.read(a);
            in.close();
            return defineClass(name, a, 0, len);
        } catch (IOException e) {
            throw new ClassNotFoundException();
        }
    }
}

O/P :
// NO GC of Classloaders :(
c1 : class Test
c2 : class Test
c1 == c2 :false
Identity hashCode of c1.s :1442407170  // Value- 1
Identity hashCode of c2.s :1442407170  // Value -2
c1.s == c2.s : true
Identity hashCode of mainMethod's s : 1442407170 // Value -3


Case : 2
//Force GC of ClassLoaders and check again.

Code :
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;

// main class
 class TestStringLiteralGC {
    public static void main(String[] args) throws Exception {
        Class<?> c1 = new CustomClassLoader().loadClass("Test"); // load class once
        Class<?> c2 = new CustomClassLoader().loadClass("Test");  // load class again
        System.out.println("c1 : " + c1); // c1 : class Test
        System.out.println("c2 : " + c2); // c2 : class Test
        System.out.println("c1 == c2 :" + (c1 == c2)); //c1 == c2 :false --> So, now we have 2 different class objects for same class.
        Field f1 = c1.getDeclaredField("s"); // getting field s of c1
        f1.setAccessible(true);
        System.out.println("Identity hashCode of c1.s :"+ System.identityHashCode(f1.get(null))); // Identity hashCode of c1.s :1442407170
        Field f2 = c2.getDeclaredField("s");  // getting field s of c2
        f2.setAccessible(true);
        System.out.println("Identity hashCode of c2.s :"+ System.identityHashCode(f2.get(null))); // Identity hashCode of c2.s :1442407170
        System.out.println("c1.s == c2.s : " + (f1.get(null) == f2.get(null))); // c1.s == c2.s : true ==> c1.s is the same "instance" as c2.s
        //Make c1 and c2 eligible for GC
        // So, now, there are no references to "abc"
        f1 = null;
        c1 = null;
        f2 = null;
        c2 = null;
       //call GC explicitly. Yes, twice.
        Thread.sleep(1000);
        System.gc();
        Thread.sleep(1000);
        System.gc();
        Thread.sleep(1000);
        // use the same string literal in main. Just to test that the same literal is being used.
        String s = "abc";
        System.out.println("Identity hashCode of mainMethod's s : " + System.identityHashCode(s)); // Identity hashCode of mainMethod's s : 1118140819 ==> Oh!!. The IDHashcodes are NOT the same
    }

}
// Our class which will be loaded
class Test {
    static String s = "abc"; // Our little hero!.The string literal.
}
//Our custom ClassLoader to load the class "Test"
class CustomClassLoader extends ClassLoader {
    // finalize() is to check if Object is unreachable (and ready for GC)
    protected void finalize() throws Throwable {
        System.out.println("CustomClassLoader finalize called.." + this);
    };

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if (!name.equals("Test")) {
            return super.loadClass(name);
        }
        try {
            InputStream in = ClassLoader
                    .getSystemResourceAsStream("Test.class");
            byte[] a = new byte[10000];
            int len = in.read(a);
            in.close();
            return defineClass(name, a, 0, len);
        } catch (IOException e) {
            throw new ClassNotFoundException();
        }
    }
}

O/P :

c1 : class Test
c2 : class Test
c1 == c2 :false
Identity hashCode of c1.s :1442407170 // Value - 1
Identity hashCode of c2.s :1442407170 // Value - 2
c1.s == c2.s : true
CustomClassLoader finalize called..CustomClassLoader@4e25154f // ClassLoader1 GCed
CustomClassLoader finalize called..CustomClassLoader@6d06d69c // ClassLoader2 GCed
Identity hashCode of mainMethod's s : 1118140819 // Value - 3 ..

关于java - java字符串文字可以被垃圾收集吗?。如果是,如何证明?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30777599/

10-12 03:46