像"abc"
这样的java 字符串文字可以被垃圾收集吗?如果是,我们如何以编程方式证明他们是GCed?
答案 0 :(得分:6)
是的,发布Java7后,如果加载它的类加载器被垃圾收集并且没有对字符串文字的引用,则可以对字符串文字进行垃圾收集。
注意:在Java -8中,您必须调用GC两次以确保ClassLoaders获得GCed(Metaspace .. pfff ..使用不同的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
(1,2)和3的相同IdentityHashCode表示相同的字符串文字 所有3个地方都使用“abc”。
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 ..
(1,2)和3的IdentityHashCodes是不同的。所以,字符串“abc” 在“main”方法中使用的字符串文字不是“abc” 当测试由2个不同的加载时,添加到字符串常量池 类加载器。
答案 1 :(得分:1)
一种简单的证明方法,就是使用From the Ruby API。通过分析器,您可以查看应用程序内存中的所有对象。