предисловие
струнныйintern
Метод может быть менее используемым и незнакомым для всех, хотя его не рекомендуется использовать в реальных проектах.intern
метод, аналогичный пул может быть реализован на уровне Java, но нам все равно нужно знать, что его принципиальный механизм — нет.
О стажерском методе
Через этот метод может быть возвращен стандартный объект String. В JVM имеет специальный пул String Constance для поддержания этих стандартных объектов. Постоянный пул является структурой HASH MAP, а также строковые объектные вызовыintern
Метод сначала проверит, существует ли уже стандартный объект строки в пуле.Если он существует, он напрямую вернет стандартный объект.Если он не существует, он создаст стандартный объект в пуле и вернет объект.
Процесс поиска выполняется с использованием строкового значения в качестве ключа, то есть для одного и того же строкового значения получается один и тот же стандартный объект.Например, на уровне Java может быть несколько строк со строковым значением «ключ1». объект, но черезintern
Все методы получают один и тот же объект.
каков эффект
Такintern
Что делает метод? Ранее мы знали, что уровень Java проходит до тех пор, пока значения строк равны.intern
Приобретенный должен быть тот же объект, то есть так называемые стандартные объекты. Такие как следующие
String st = new String("hello world");
String st2 = new String("hello world");
System.out.println(st.intern() == st2.intern());
Нашли? мы можем использовать==
Давайте сравним значения двух объектов, Вы должны знать, что в Java это сравнение может только определить, являются ли они одной и той же ссылкой, но поintern
После того, как метод обработан, его можно напрямую сравнить таким образом, по сравнению сequals
Но это намного быстрее, и производительность повышается. Вы можете сказать да, потому чтоintern
сделали что-то вродеequals
Операция сравнения сделана, здесь она еще будет очень трудоемкой! Да, вы правы, но если я буду делать несколько сравнений позже, разве это не преимущество, просто сделайте это один раз?equals
Все сравнения можно использовать позже==
Сделайте быстрое сравнение.
Кроме того, эффект экономии памяти также может быть достигнут в некоторых сценариях, таких как сохранение большого количества строковых объектов, которые могут повторяться, например, 100 000 строковых объектов и 90 000 строковых объектов с одним и тем же строковым значением, а затем передачаintern
Этот метод может уменьшить количество строковых объектов до 10 000, а объекты с одинаковым значением используют один и тот же стандартный объект.
Присоединиться к пулу констант времени выполнения
Есть два способа добавить строковые объекты в пул констант времени выполнения на уровне Java:
- Используйте двойные кавычки непосредственно в программе, чтобы объявить строковый объект, и объект будет добавлен в пул констант во время выполнения. Например, после того, как класс скомпилирован в байт-код, во время выполнения есть соответствующие инструкции по добавлению его в пул констант.
public class Test{
public static void main(String[] args){
String s = "hello";
}
}
- Другой - через класс String
intern
метод, он может определить, существует ли уже текущая строка в пуле констант, и если нет, то добавить ее в пул констант. Например ниже,
String s = new String("hello");
s.intern();
другой пример
JDK9.
public class Test {
public static void main(String[] args) {
String s = new String("hello");
String ss = new String("hello");
System.out.println(ss == s);
String sss = s.intern();
System.out.println(sss == s);
String ssss = ss.intern();
System.out.println(ssss == sss);
System.out.println("=========");
String s2 = "hello2";
String ss2 = new String("hello2");
System.out.println(ss2 == s2);
String sss2 = s2.intern();
System.out.println(sss2 == s2);
String ssss2 = ss2.intern();
System.out.println(ssss2 == sss2);
}
}
false
false
true
=========
false
true
true
Реализация постоянного пула
Слой Java очень прост, простоintern
Определен как собственный метод.
public native String intern();
соответствуетJVM_InternString
функционируют, главным образом, за счетJNIHandles::resolve_non_null
Функция преобразуется в указатель oop уровня JVM, а затем вызываетсяStringTable::intern
Функция получает окончательный возвращенный объект и, наконец, передаетJNIHandles::make_local
Преобразование в объект уровня Java и возврат.
JNIEXPORT jobject JNICALL
Java_java_lang_String_intern(JNIEnv *env, jobject this)
{
return JVM_InternString(env, this);
}
JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str))
JVMWrapper("JVM_InternString");
JvmtiVMObjectAllocEventCollector oam;
if (str == NULL) return NULL;
oop string = JNIHandles::resolve_non_null(str);
oop result = StringTable::intern(string, CHECK_NULL);
return (jstring) JNIHandles::make_local(env, result);
JVM_END
В основном см.StringTable::intern
, StringTable — это пул констант, используемый средой выполнения JVM для хранения констант. Его структура представляет собой хеш-карту, примерно как показано на следующем рисунке.
Основная логика состоит в том, чтобы сначала вычислить длину кодировки юникода, соответствующей строке в кодировке utf-8, создать новый массив в соответствии с длиной, требуемой кодировкой юникода, преобразовать строку в кодировку юникода и, наконец, вызвать другойintern
функция.
oop StringTable::intern(const char* utf8_string, TRAPS) {
if (utf8_string == NULL) return NULL;
ResourceMark rm(THREAD);
int length = UTF8::unicode_length(utf8_string);
jchar* chars = NEW_RESOURCE_ARRAY(jchar, length);
UTF8::convert_to_unicode(utf8_string, chars, length);
Handle string;
oop result = intern(string, chars, length, CHECK_NULL);
return result;
}
Логика следующая,
- пройти через
java_lang_String::hash_code
Получите хэш-значение. - Вызывается по хеш-значению
lookup_shared
Функция смотрит, имеет ли общая хеш-таблица значение строкового объекта, если есть прямой возврат к найденному объекту, функция будет вызываться косвенноlookup
функция, которая будет дополнительно проанализирована позже. - Используется ли другой алгоритм хеширования, и если да, то пересчитать хэш-значение.
- пройти через
hash_to_index
Функция вычисляет значение индекса, соответствующее хеш-значению. - пройти через
lookup_in_main_table
Функция ищет строковый объект в корзине, соответствующей значению индекса, и возвращает объект, если он найден. - Если в хэш-таблице ничего из вышеперечисленного не найдено, необходимо добавить его в таблицу, использовать
MutexLocker
закрой, потом позвониbasic_add
Функция завершает операцию сложения, и функция будет дополнительно проанализирована позже. - Возвращает строковый объект.
oop StringTable::intern(Handle string_or_null, jchar* name,
int len, TRAPS) {
unsigned int hashValue = java_lang_String::hash_code(name, len);
oop found_string = lookup_shared(name, len, hashValue);
if (found_string != NULL) {
return found_string;
}
if (use_alternate_hashcode()) {
hashValue = alt_hash_string(name, len);
}
int index = the_table()->hash_to_index(hashValue);
found_string = the_table()->lookup_in_main_table(index, name, len, hashValue);
if (found_string != NULL) {
if (found_string != string_or_null()) {
ensure_string_alive(found_string);
}
return found_string;
}
Handle string;
if (!string_or_null.is_null()) {
string = string_or_null;
} else {
string = java_lang_String::create_from_unicode(name, len, CHECK_NULL);
}
oop added_or_found;
{
MutexLocker ml(StringTable_lock, THREAD);
added_or_found = the_table()->basic_add(index, string, name, len,
hashValue, CHECK_NULL);
}
if (added_or_found != string()) {
ensure_string_alive(added_or_found);
}
return added_or_found;
}
Постоянный пул — это хэш-таблица, так каково же количество сегментов по умолчанию? Глядя на приведенные ниже определения, по умолчанию он равен 60013 в 64-разрядных системах и 1009 в 32-разрядных системах.
const int defaultStringTableSize = NOT_LP64(1009) LP64_ONLY(60013);
Логика поиска хеш-таблицы такова:
- Хеш-значение вычисляется путем взятия остатка от количества сегментов для получения индекса.
- Получите соответствующую информацию о сегменте с помощью индекса.
- Получите смещение ковша.
- Получите тип ведра.
- Получить вход.
- если
VALUE_ONLY_BUCKET_TYPE
типа ведро, объект, соответствующий смещению, декодируется напрямую.Каждая запись в этом типе записей имеет только один 4 байта для представления смещения, то естьu4 offset;
. - Если это обычный тип корзины, просмотрите запись, чтобы найти смещение, соответствующее записи с указанным хеш-значением, а затем декодируйте объект, соответствующий смещению. Каждая запись в записях имеет 8 байт, а структура
u4 hash;union {u4 offset; narrowOop str; }
, которому предшествует хеш-значение, за которым следует смещение или указатель символьного объекта. - Два разных типа структур могут быть просто показаны следующим образом: первое ведро и третье ведро являются общими типами, указывая на множество записей, состоящих из [хэш + смещение], в то время как второе ведро
VALUE_ONLY_BUCKET_TYPE
введите, указывая прямо на [смещение].
buckets[0, 4, 5, ....]
| | |
| | +---+
| | |
| +----+ |
v v v
entries[H,O,H,O,O,H,O,H,O.....]
template <class T, class N>
inline T CompactHashtable<T,N>::lookup(const N* name, unsigned int hash, int len) {
if (_entry_count > 0) {
int index = hash % _bucket_count;
u4 bucket_info = _buckets[index];
u4 bucket_offset = BUCKET_OFFSET(bucket_info);
int bucket_type = BUCKET_TYPE(bucket_info);
u4* entry = _entries + bucket_offset;
if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
T res = decode_entry(this, entry[0], name, len);
if (res != NULL) {
return res;
}
} else {
u4* entry_max = _entries + BUCKET_OFFSET(_buckets[index + 1]);
while (entry < entry_max) {
unsigned int h = (unsigned int)(entry[0]);
if (h == hash) {
T res = decode_entry(this, entry[1], name, len);
if (res != NULL) {
return res;
}
}
entry += 2;
}
}
}
return NULL;
}
Логика добавления хеш-таблицы следующая:
- Используются ли другие хэш-алгоритмы, и если да, пересчитайте значение хеш-функции и рассчитайте соответствующее значение индекса.
- пройти через
lookup_in_main_table
Функция проверяет, существует ли строковое значение в хэш-таблице. - Создать запись, включая хеш-значение и указатель строкового объекта.
- пройти через
add_entry
функция добавляется в хеш-таблицу. - Возвращает строковый объект.
oop StringTable::basic_add(int index_arg, Handle string, jchar* name,
int len, unsigned int hashValue_arg, TRAPS) {
NoSafepointVerifier nsv;
unsigned int hashValue;
int index;
if (use_alternate_hashcode()) {
hashValue = alt_hash_string(name, len);
index = hash_to_index(hashValue);
} else {
hashValue = hashValue_arg;
index = index_arg;
}
oop test = lookup_in_main_table(index, name, len, hashValue);
if (test != NULL) {
return test;
}
HashtableEntry<oop, mtSymbol>* entry = new_entry(hashValue, string());
add_entry(index, entry);
return string();
}
-XX:StringTableSize
Как упоминалось ранее, размер сегмента хеш-таблицы JVM по умолчанию: 60013 для 64-битных систем и 1009 для 32-битных систем. Если мы хотим изменить его размер, мы можем установить-XX:StringTableSize
для достижения эффекта.
-XX:+PrintStringTableStatistics
Если вы хотите увидеть статистику, связанную с постоянным пулом, можно установить-XX:+PrintStringTableStatistics
,那么 JVM 停止时就会输出相关信息了。 Например,
SymbolTable statistics:
Number of buckets : 20011 = 160088 bytes, avg 8.000
Number of entries : 20067 = 481608 bytes, avg 24.000
Number of literals : 20067 = 838520 bytes, avg 41.786
Total footprint : = 1480216 bytes
Average bucket size : 1.003
Variance of bucket size : 0.994
Std. dev. of bucket size: 0.997
Maximum bucket size : 8
StringTable statistics:
Number of buckets : 60013 = 480104 bytes, avg 8.000
Number of entries : 1003077 = 24073848 bytes, avg 24.000
Number of literals : 1003077 = 48272808 bytes, avg 48.125
Total footprint : = 72826760 bytes
Average bucket size : 16.714
Variance of bucket size : 9.683
Std. dev. of bucket size: 3.112
Maximum bucket size : 30
------------- Рекомендуем прочитать ------------
Резюме моей статьи за 2017 год — машинное обучение
Краткое изложение моих статей за 2017 год — Java и промежуточное ПО
Резюме моих статей 2017 года — глубокое обучение
Краткое изложение моих статей за 2017 год — исходный код JDK
Резюме моей статьи за 2017 год — обработка естественного языка
Резюме моих статей 2017 года — Java Concurrency
------------------рекламное время----------------
Поговори со мной, задай мне вопросы:
Меню официальной учетной записи было разделено на «распределенное», «машинное обучение», «глубокое обучение», «НЛП», «глубина Java», «ядро параллелизма Java», «исходный код JDK», «ядро Tomcat», и т.д. Там может быть один стиль, чтобы удовлетворить ваш аппетит.
Зачем писать «Анализ проектирования ядра Tomcat»
Добро пожаловать, чтобы следовать: