Sai*_*S.R 23 java string performance jvm pool
我很好奇,为什么字符串可以在不调用创建new String(),作为API提到它是Object的class java.lang.String
那么我们如何才能使用String s="hi"而不是String s=new String("hi")?
这篇文章澄清了==运算符的使用和缺席,new 并说这是由于String文字被实际内容或从文字池中获取JVM,因此Strings是不可变的.
看到诸如此类的陈述
String s="hi"
Run Code Online (Sandbox Code Playgroud)
第一次真正发生了什么?
是否JVM像这样替换它String s=new String("hi"),其中创建了一个Object并将"hi"其添加到String文本池中,因此后续调用如String s1="hi"从池中获取?
这是底层机制的运作方式吗?如果是的话,那就是
String s=new String("Test");
String s1="Test";
Run Code Online (Sandbox Code Playgroud)
同样的
String s="Test";
String s1="Test";
Run Code Online (Sandbox Code Playgroud)
在内存利用率和效率方面?
另外,有什么方法可以访问字符串池来检查它中有多少String文字,占用了多少空间等等?
Jes*_*per 15
Java编译器对字符串文字有特殊支持.假设没有,那么在源代码中创建字符串真的很麻烦,你必须写下这样的东西:
// Suppose that we would not have string literals like "hi"
String s = new String(new char[]{ 'h', 'i' });
Run Code Online (Sandbox Code Playgroud)
回答你的问题:
或多或少,如果你真的想知道细节,你必须研究JVM的源代码,你可以在OpenJDK找到它,但要注意它是巨大而复杂的.
不,这两个不相同.在第一种情况下,您将显式创建一个新String对象:
String s=new String("Test");
Run Code Online (Sandbox Code Playgroud)
它将包含String由文字表示的对象的副本"Test".请注意,用Java 编写绝不是一个好主意new String("some literal")- 字符串是不可变的,并且永远不需要复制字符串文字.
我不知道检查字符串池中的内容.
The*_*ind 10
String s="hi"第一次真正发生了什么?JVM是否像这样替换它
String s=new String("hi"),其中创建了一个Object并且"hi"被添加到String文本池中,因此后续调用如String s1 ="hi"从池中获取?
不会.真正发生的事情是 - 字符串文字在编译期间被解析,并且在加载/初始化或懒惰时加密(添加到字符串常量池).因此,它们可供JVM中的类使用.请注意,即使在字符串常量池中有一个带有值的String,也会在堆上创建另一个String并返回其引用."hi"new String("hi")
- 是
String s=new String("Test");
String s1="Test";
Run Code Online (Sandbox Code Playgroud)
同样的
String s="Test";
String s1="Test";
Run Code Online (Sandbox Code Playgroud)
在内存利用率和效率方面?
不,在第一种情况下,创建2个"测试"字符串.一个将被添加到String常量池(假设它不存在)和另一个在堆上.第二种情况可以是GCed.在第二种情况下,String常量池中只有一个字符串文字,并且有2个引用它(s和s1).
- 另外,如果有任何方法我们可以访问字符串池,检查它中存在多少字符串文字,空间占用等来自程序或任何监视工具?
我认为我们不能看到String常量池的内容.我们只能基于我们的假设来假设和确认行为.
以下是略微简化,因此不要试图引用它的确切细节,但一般原则适用.
每个编译的Java类都包含一个数据blob,它指示在该类文件中声明了多少字符串,每个字符串的长度以及属于所有字符串的字符.加载类时,类加载器将创建一个String[]合适的大小来保存该类中定义的所有字符串; 对于每个字符串,它将生成一个char[]合适的大小,从类文件中读取适当数量的字符到其中char[],创建一个String封装这些字符,并将引用存储到类中String[].
在编译某个类(例如Foo)时,编译器知道它遇到的第一个,第二个,第三个,第五个等myString = "George";字符串文字.如果代码说并且George是第六个字符串文字,那么它将在代码中显示为"加载字符串文字#6" "指导; 即时编译器在为该指令生成代码时,将生成一条指令以获取与该类关联的第六个字符串引用.
这与主题没有密切关系,但每当你怀疑java编译器会做什么时,你可以使用
javap -c CompiledClassName
Run Code Online (Sandbox Code Playgroud)
打印实际发生的事情.(来自dir的CompiledClassName,其中CompiledClassName.class是)
要添加到Jesper的答案,有更多的机制在起作用,比如当你从文字或最终变量连接一个字符串时,它仍将使用实习池:
String s0 = "te" + "st";
String s1 = "test";
final String s2 = "te";
String s3 = s2 + "st";
System.out.println(s0==s1); //true
System.out.println(s3==s1); //true
Run Code Online (Sandbox Code Playgroud)
但是当你使用非final变量连接时,它将不使用池:
String s0 = "te";
String s1 = s0 + "st";
String s2 = "test";
System.out.println(s1 == s2); //false
Run Code Online (Sandbox Code Playgroud)
一种,但不完全是.
在常量池解析期间创建并实现字符串常量.这在第一次执行LDC加载字符串文字的字节码时发生.在第一次执行之后,JVM将JVM_CONSTANT_UnresolvedString常量池标记替换为JVM_CONSTANT_String标记,以便下次LDC将使用现有字符串而不是创建新字符串.
"Test"不会.第一次使用会创建一个新的字符串对象.然后new String("Test")将创建第二个对象.
是的,使用HotSpot Serviceability Agent.这是一个例子.