Stringach w Javie
by jgardo.dev
| Case | Rank | Count | Retained size [MiB] |
| Hello world | 1 | 21 580 | 1,294 |
| Pet Clinic (Spring) | 3 | 118 083 | 6,422 |
| Real prod app | 4 | 361 748 | 56,858 |
char typechar charAt()String replace(char old, char new)char[] toCharArray()boolean contains(CharSequence s)char interpreted as UTF-16codePoint is considered as base character representationint codePointAt(int index)int indexOf(int ch)Character.toChars(int codePoint)Character.toCodePoint(char high, char low)java.lang.String
public final class String
implements Serializable,
Comparable<String>,
CharSequence,
Constable, ConstantDesc {
@Stable private final byte[] value;
(...)
}
public final class String (...) {
@Stable
private final byte[] value;
private final byte coder; // LATIN1 = 0 or UTF16 = 1
(...)
public char charAt(int index) {
return this.isLatin1()
? StringLatin1.charAt(this.value, index)
: StringUTF16.charAt(this.value, index);
}
public int codePointBefore(int index) {
int i = index - 1;
checkIndex(i, this.length());
return this.isLatin1()
? this.value[i] & 255
: StringUTF16.codePointBefore(this.value, index);
}
public void getChars(...) {
checkBoundsBeginEnd(...);
checkBoundsOffCount(...);
if (this.isLatin1()) {
StringLatin1.getChars(...);
} else {
StringUTF16.getChars(...);
}
}
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
} else {
if (anObject instanceof String) {
String aString = (String)anObject;
if ((!COMPACT_STRINGS
|| this.coder == aString.coder)
&& StringLatin1.equals(
this.value, aString.value)) {
return true;
}
}
}
}
}
.hashcode()
public final class String {
@Stable
private final byte[] value;
private final byte coder; // LATIN1 = 0 or UTF16 = 1
private int hash; // Default to 0
private boolean hashIsZero; // Default to false;
// sample zero-hashcoded value
// "ARcZguv" or just "\u0000"
public int hashCode() {
int h = this.hash;
if (h == 0 && !this.hashIsZero) {
h = this.isLatin1()
? StringLatin1.hashCode(this.value)
: StringUTF16.hashCode(this.value);
if (h == 0) { this.hashIsZero = true; }
else { this.hash = h; }
}
return h;
}
}
final class StringLatin1 {
public static int hashCode(byte[] value) {
return switch (value.length) {
case 0 -> 0;
case 1 -> value[0] & 0xff;
default -> ArraysSupport.vectorizedHashCode(
value, 0, value.length,
0, ArraysSupport.T_BOOLEAN);
};
}
}
public class ArraysSupport {
@IntrinsicCandidate
public static int vectorizedHashCode(...)
return switch (basicType) {
(...)
case T_CHAR -> array instanceof byte[]
? utf16hashCode(initialValue,
(byte[]) array, fromIndex, length)
: hashCode(initialValue,
(char[]) array, fromIndex, length);
}
}
private static int hashCode(int result, char[] a,
int fromIndex, int length) {
int end = fromIndex + length;
for (int i = fromIndex; i < end; i++) {
result = 31 * result + a[i];
}
return result;
}
}
private static int hashCode() {
for (int i = 0; i < value.length; i++) {
result = 31 * result + a[i];
}
return result;
}
31 * ('C' - 'D') == ('B' - 'a')
"common_prefixDB".hashCode() == "common_prefixCa".hashCode()
s[0]* 31⁽ⁿ⁻¹⁾ + s[1] * 31⁽ⁿ⁻²⁾ + ... + s[n−1] =
public final class String (...) {
@Stable private final byte[] value;
private final byte coder; // LATIN1 = 0 or UTF16 = 1
private int hash;
private boolean hashIsZero;
}
// an object header (12B)
// a reference to a byte[] (4B)
// a coder field (1B)
// an int hash (4B)
// a boolean hashIsZero (1B)
// Summary: 22B -> 24B
// + size of value array = 12B + 4B length + byte array
public final class String (...) {
@Stable private final byte[] value;
private final byte coder;
private int hash; // initially 0
private boolean hashIsZero; // initially false
}
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
class HelloWorldHolder {
public String hello = "Hello World!";
}
Constant pool:
#7 = String #8 // Hello World!
#8 = Utf8 Hello World!
Code:
0: getstatic #15 // Field System.out
3: ldc #7 // String Hello World!
5: invokevirtual #21 // Method PrintStream.println
Handle java_lang_String::create_from_symbol(
Symbol* symbol, TRAPS) {
const char* utf8_str = (char*)symbol->bytes();
int utf8_len = symbol->utf8_length();
bool has_multibyte, is_latin1;
int length = UTF8::unicode_length(utf8_str, utf8_len,
is_latin1, has_multibyte);
Handle h_obj = basic_create(length, is_latin1, ...);
(...)
}
Handle java_lang_String::basic_create(int length,
bool is_latin1, TRAPS) {
(...)
// Create the String object first,
// so there's a chance that the String
// and the char array it
// points to end up in the same cache line.
oop obj;
obj = vmClasses::String_klass()
->allocate_instance(CHECK_NH);
Handle h_obj(THREAD, obj);
int arr_length = is_latin1 ?
length : length << 1; // 2 bytes per UTF16.
(...)
}
-XX:StringTableSizeString.hashCode()String.intern()
String.intern() - why not?public native String intern();
class Test {
public static void main(String[] args) {
String constantConcat = "Hello" + ' ' + "World!";
}
}
Constant pool:
#7 = String #8 // Hello World!
#8 = Utf8 Hello World!
public static void main(java.lang.String[]);
0: ldc #7 // String Hello World!
2: astore_1
3: return
String name = args[0];
String standardConcat = "Hello " + name + "!";
StringBuffer
do Javy 1.4
StringBuilder
do Javy 1.8
class Test {
public static void main(String[] args) {
String standardConcat = "Hello " + args[0] + "!";
}
}
4: invokespecial #3 // StringBuilder."<init>"()
7: ldc #4 // String Hello
9: invokevirtual #5 // StringBuilder.append();
12: aload_0
13: iconst_0
14: aaload
15: invokevirtual #5 // StringBuilder.append();
18: ldc #6 // String !
20: invokevirtual #5 // StringBuilder.append();
23: invokevirtual #7 // StringBuilder.toString();
class Test {
public static void main(String[] args) {
String standardConcat = "Hello " + args[0] + "!";
}
}
1: iconst_0
2: aaload
3: invokedynamic #2, 0 // InvokeDynamic #0;
8: astore_1
BootstrapMethods:
0: #22 REF_invokeStatic StringConcatFactory
.makeConcatWithConstants:(...) CallSite;
Method arguments:
#23 Hello \u0001!
class Test {
public static void main(String[] args) {
String highArityConcat = f0+","+ f1+","+ f2+"," (...)
+ f10+","+ f11+","+ f12+","+ f13+","+ f14 (...)
+ f20+","+ f21+","+ f22+","+ f23+","+ f24 (...)
+ (...)
+ f120+","+ f121+","+ f122;
}
}
public String(char value[]);
public String(int[] codePoints, int offset, int count);
public String(byte[] bytes,int offset,int length,Charset);
public String(StringBuilder builder);
public String() {
this.value = "".value;
this.coder = "".coder;
}
@IntrinsicCandidate
public String(String original) {
this.value = original.value;
this.coder = original.coder;
this.hash = original.hash;
}
String key = "The quick brown fox jumps over the lazy dog"
.substring(4,5);
someHashMap.put(key, "value");
public final class String (...) {
@Stable private final byte[] value;
private final byte coder;
private int hash;
private boolean hashIsZero;
}
CharSequenceStringBuilderCharBufferbyte[]Reader/InputStream/nio alternative (jeśli za duże
Stringi)
hash and hashIsZero non-final fields-XX:+UseCompactObjectHeaders|
Feedback |
Prezentacja |
|---|