Conjunto de constantes de string, ler isso é suficiente (1)

Olá, eu sou Ziya. Com mais de dez anos de carreira técnica, ele superou dificuldades desde novato técnico até diretor técnico, especialista em JVM e empreendedorismo. Pilhas de tecnologia como assembly, linguagem C, C++, kernel do Windows, kernel do Linux. Eu gosto especialmente de estudar a implementação subjacente de máquinas virtuais e tenho uma pesquisa aprofundada sobre JVM. Os artigos compartilhados são hard-core, muito difíceis.


Hands over JVM, pool de memória, algoritmo de coleta de lixo, sincronizado, pool de threads, NIO, algoritmo de marcação de três cores...

O que você vai falar com você hoje? Conjunto de constantes de string, ou seja, como as strings no código Java são armazenadas na JVM.

Ao analisar um problema, precisamos destilar um problema altamente abstrato em um problema que busca a essência. Quando se trata de raciocínio, o segredo para derrotar o inimigo com um movimento é responder às mudanças com a mesma coisa. Esses dois pedaços de código são a base para você jogar com strings Java. O termo Go é chamado de "olho de xadrez", que significa o círculo de strings no mundo Java. Todas as alterações são derivadas desses dois pedaços de código.

analise de problemas

Existem duas perspectivas ao estudar as coisas: a perspectiva do pesquisador e a perspectiva do designer. A perspectiva do pesquisador significa que partimos da perspectiva do aprendizado, seguimos a trajetória das coisas, nos aprofundamos na trajetória, e falar a linguagem humana é ler o código-fonte para entender a ideia do designer. O ponto de vista do designer significa que imaginamos que vamos fazer uma coisa, como vamos fazer, quais opções estão à frente, analisamos os prós e contras de cada opção e, finalmente, fazemos uma troca.

Acredito que na maioria das vezes todo mundo estuda os problemas na perspectiva dos pesquisadores, hoje vamos mudar nosso pensamento e estudar esse problema na perspectiva dos designers.

Resumido em uma questão essencial: se fôssemos escrever uma JVM, como lidaríamos com strings. Este problema é muito simples, use uma tabela de hash, ou seja, uma tabela de hash. Deve-se notar aqui que existem duas estruturas de tipo hashtable no mundo Java: HashTable e HashMap do Java, que são implementados em Java puro; e hashtable do Hotspot, que é implementado em C++ puro. O que estamos discutindo hoje é a tabela de hash no código fonte do Hotspot.

Quando muitos amigos veem C++, eles ficam com medo... ou pensam: isso é algo que eu posso assistir sem gastar dinheiro? Isso é algo que eu posso assistir neste nível? Eu realmente entendo...

Uh, não tenha medo, não há código-fonte hoje! Apenas Ziya aprendeu a principal animação pequena desenhada por AE por um longo tempo, o que pode ajudá-lo a compreendê-la com precisão, rapidez e precisão. Bem, não desanime se você quiser ver o código-fonte, o próximo artigo é código-fonte puro, para satisfazê-lo. Desde que você queira ver, não é impossível eu capotar o carro de propósito, deixar uma mensagem e responder: quero ver o capotamento

tabela de hash

Para garantir a integridade do artigo, falarei detalhadamente sobre o hashtable. Se você acha que esta peça é muito familiar para você, também é recomendável lê-la novamente, deve ser recompensada

Em primeiro lugar, vamos ver como funciona o hashtable, os substantivos e fenômenos envolvidos, e vamos falar sobre isso em detalhes mais tarde.

Falando sobre a implementação subjacente do hashtable, existem dois métodos principais: array + lista encadeada simples, array + árvore vermelho-preto. Você pode insistir que existe um terceiro tipo: array + lista encadeada simples + árvore vermelho-preta. De repente, você pode ter muitas perguntas: por que você quer fazer isso? Qual é a diferença entre as diferentes implementações? Não se preocupe, eu vou falar sobre isso.

Primeiro, entenda como funciona o array + a lista vinculada individualmente. Nossa imagem é um array + uma lista encadeada simples.

Passo 1: Quando a string ziya chega, ela é saudada pelo algoritmo de hash. O algoritmo de hash também é chamado de algoritmo de hash. Há muitas maneiras de realizá-lo. Não vou entrar em detalhes aqui. Os parceiros interessados ​​podem usar o Baidu sozinhos. O efeito obtido é gerar o índice de buckets. Se a string ziya for calculada pelo algoritmo de hash, o índice de buckets será igual a 2.

Etapa 2: a estrutura de dados dos buckets é uma matriz. O primeiro passo é gerar o subscrito do array. Neste momento, a string ziya será encapsulada em um nó de lista vinculada Node, onde a string ziya é o primeiro elemento de index=2, o nó de lista vinculada Node encapsulado pela string ziya será usado como o cabeçalho da lista vinculada e o endereço de memória do Node será armazenado em onde index=2.

Neste momento, temos que pensar em um problema: buckets são arrays.Qualquer um que tenha escrito código C++ sabe que um array não pode ser criado sem especificar o comprimento do array. Qual o tamanho da designação? O código-fonte do ponto de acesso é 20011. Por que 20011? Eu também tenho essa dúvida, mas tentei encontrar a resposta e não consegui, mas tenho certeza que esse número deve ter um significado especial. Amigos que sabem disso podem deixar uma mensagem para me avisar. Claro, eu não vou desistir da busca da verdade!

接下来我们用发展的眼光看问题:如果存储的字符串经过hash算法得到的buckets index不重复,最多可以存储20011个字符串。如果超过20011个字符串呢?这时候肯定会出现一种情况:不同的字符串经过hash算法运算得到的buckets index是相同的,这种情况叫hash碰撞。

比如图中的字符串[手写]与字符串[JVM],它们经hash算法运算得到的buckets index都是0,这时候怎么存储呢?先把字符串[手写]封装成链表节点Node,因为是index=0的第一个节点,将该Node的内存地址写入index=0的位置。然后把字符串[JVM]封装成链表节点Node,插入链表尾部。注意:链表的节点在内存中不是连续的!

后面有字符串写入,就是重复这个动作了:字符串到来,经过hash算法运算得到buckets index,然后将字符串封装成链表节点Node。如果到来的字符串是该index的第一个元素,将节点的内存地址写入该index位置,如果不是第一个元素,插入到链表尾部。

一种特殊情况

先看图,再说话

该图想表达的现象是buckets index=0对应的链表深度过深,达到了345,这样如果我们查找的字符串恰好是[内功],抛开其他运算,就光遍历链表都要345次。对于现在的高性能计算机来说,345次好像也不慢,但是性能随数据增长而出现性能下降,意味着总有一天会达到性能瓶颈,需要优化。

于是就出现了数组+红黑树结构,后面又发展出了数组+链表+红黑树结构。我解释下原因:大量数据的情况下,红黑树的查找性能比链表高。那为什么后面又会出现数组+链表+红黑树结构呢?注意看前提:大量数据的情况。当数据量比较少的时候,比如拿HashMap底层实现来说,这个阈值是8,即hash碰撞严重情况下,数据少于8,两者性能上差异不大,但是链表相对实现起来更容易、占用空间更少…

hash碰撞严重的情况,改变底层存储容器是一种思路,还有没有其他思路呢?有!改变hash算法,Hotspot源码就是这样干的。默认算法是java_lang_String::hash_code,触发rehash后使用的算法是AltHashing::murmur3_32。

Hotspot触发rehash的条件是:查找一个字符串超过100次。

串一串

JVM中与字符串相关的有两个表,一个是SymbolTable,一个是StringTable。我们通常说的字符串常量池是指StringTable。但是StringTable的运行与SymbolTable紧密相连。这篇文章讲到的内容,全部都是SymbolTable的底层原理。关于它俩之间的联系,本文篇幅已经够长了,放下篇文章讲。

SymbolTable底层是基于hashtable实现的,结构是数组+链表,当存储的数据足够多,遇到hash碰撞严重时,是通过切换hash算法实现的。默认算法是java_lang_String::hash_code,触发rehash后使用的算法是AltHashing::murmur3_32。

留个问题吧:切换hash算法不会马上生效,需要等到安全点,你觉得是为什么?欢迎留言讨论

我是子牙老师,喜欢钻研底层,深入研究Windows、Linux内核、JVM。如果你也喜欢研究底层,欢迎关注我的公众号 【道格子牙】

Acho que você gosta

Origin juejin.im/post/7087772381443260452
Recomendado
Clasificación