Can Java string literals be garbage collected? If so, how to prove it?
•
Java
Can Java string literals like "ABC" be garbage collected? If so, how can we programmatically prove that they are GCed?
Solution
Yes, when Java 7 is released, if the class loader that loads it is garbage collected and there is no reference to the string text, the string text can be garbage collected
Note: in Java - 8, you must call GC twice to ensure that classloaders get GCed (Metaspace.. pfff.. using a different GC won't help)
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,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,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,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 ..
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
二维码