java字符串文字可以被垃圾收集吗?如果是,如何证明? [英] Can java string literals be garbage collected?. If Yes, how to prove it?
问题描述
java 字符串文字可以像abc
那样被垃圾收集吗?如果是,我们如何以编程方式证明他们是GCed?
Can java String literals like "abc"
be garbage collected?. If yes, how can we programatially prove that they are GCed?
推荐答案
是的,发布Java7,如果加载它的类加载器被垃圾收集并且没有对字符串文字的引用,则可以对字符串文字进行垃圾收集。
Yes, post Java7, String literals can be garbage collected if the class loader which loaded it gets garbage collected and there are no references to the string literal.
注意:在Java -8中,您将必须调用GC两次以确保ClassLoaders获得GCed(Metaspace .. pfff ..使用不同的GC将无济于事。)
Note : In Java -8, you will have to call GC twice in order 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, 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
相同的IdentityHashCode(1,2)和3表示相同的字符串文字
abc正在所有3个地方使用。
Same IdentityHashCode for (1,2) and 3 means the same string literal "abc" is being used in all 3 places.
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是不同的。因此,main方法中使用的字符串abc
与字符串文字abc不同,当由2个不同的
类加载器加载Test时,
被添加到字符串常量池中。
IdentityHashCodes for (1,2) and 3 are different. So, the string "abc" used in "main" method is not the same string literal "abc" which was added to string constants pool when Test was loaded by 2 different classloaders.
这篇关于java字符串文字可以被垃圾收集吗?如果是,如何证明?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!