Java Constant Pool é um tópico duradouro e também é o favorito do entrevistador. Existem muitos tipos de perguntas. Vou resumir desta vez.
teoria
Primeiro, vamos expressar a distribuição de memória virtual da JVM:
O contador do programa é um pipeline para a JVM executar o programa, armazenando algumas instruções de salto. Isso é muito profundo e eu não entendo.
A pilha de métodos local é a pilha usada pela JVM para chamar os métodos do sistema operacional.
A pilha da máquina virtual é a pilha usada pela JVM para executar o código Java.
A área do método armazena algumas constantes, variáveis estáticas, informações de classe etc., que podem ser entendidas como o local de armazenamento do arquivo de classe na memória.
A pilha da máquina virtual é a pilha usada pelo JVM para executar o código Java.
Piscinas constantes em Java são realmente divididas em duas formas: piscinas constantes estáticas e piscinas constantes de tempo de execução .
O chamado pool constante estático é o pool constante no arquivo *.class. O pool constante no arquivo de classe não apenas contém string (número) literais, mas também contém informações sobre classes e métodos, ocupando a maior parte do espaço do arquivo de classe.
O pool constante de tempo de execução é a máquina virtual da JVM carrega o pool constante no arquivo de classe na memória após concluir a operação de carregamento da classe e salvá -lo na área do método . A piscina constante que costumamos chamar refere -se ao pool constante de tempo de execução na área de métodos.
Em seguida, citamos alguns exemplos de piscinas constantes que são populares na Internet e depois os explicamos.
String S1 = "Hello"; String s2 = "hello"; String s3 = "hel" + "lo"; String s4 = "hel" + new string ("lo"); String S5 = new String ("Hello"); String s6 = s5.intern (); String s7 = "h"; String s8 = "ello"; String s9 = s7 + s8; system.out.println (s1 == s2); // truesystem.out.println (s1 == s3); // truesystem.out.println (s1 == s4); // Falsesystem.out.println (S1 == S9); // Falsesystem.out.println (s4 == S5); // Falsesystem.out.println (S1 == S6); // verdadeiroPrimeiro de tudo, em Java, o operador == é usado diretamente e os endereços de referência de duas strings são comparados, não o conteúdo. Use String.Equals () para comparar o conteúdo.
S1 == S2 é muito fácil de entender. Quando S1 e S2 são atribuídos, eles usam literais de cordas. Para ser franco, eles escrevem diretamente a corda até a morte. Durante a compilação, esse literal será colocado diretamente no pool constante do arquivo de classe, realizando a reutilização. Depois de carregar o pool constante em tempo de execução, S1 e S2 apontam para o mesmo endereço de memória, para que sejam iguais.
Há um poço em S1 == S3. Embora o S3 seja uma corda unida dinamicamente, todas as partes envolvidas na emenda são literais conhecidas. Durante o período de compilação, esse splicing será otimizado e o compilador o ajudará diretamente a secá -lo. Portanto, String S3 = "HEL" + "LO"; é otimizado para string s3 = "hello"; No arquivo de classe, então S1 == S3 é verdadeiro.
S1 == S4, obviamente, não é igual. Embora o S4 também seja emendado, a parte da nova string ("LO") não é uma parte literal conhecida, mas uma parte imprevisível. O compilador não o otimizará. Você deve esperar até a execução para determinar o resultado. Combinado com o teorema de invariância da string , você sabe onde o S4 é alocado, portanto o endereço deve ser diferente. Uma breve imagem para esclarecer a ideia:
S1 == S9 não é igual e o motivo é semelhante. Embora os literais de cordas usados por S7 e S8 ao atribuir valores, quando a emenda em S9, S7 e S8 são imprevisíveis. Afinal, o compilador é um compilador e não pode ser usado como intérprete, portanto não é otimizado. Quando é executado, a nova corda emendada em S7 e S8 não tem certeza na pilha e não pode ser a mesma que o endereço S1 no pool constante da área do método.
S4 == S5 não é mais necessário ser explicado, definitivamente não é igual, ambos estão na pilha, mas os endereços são diferentes.
A igualdade de S1 == S6 é completamente atribuída ao método estagiário. S5 está na pilha e o conteúdo é olá. O método da Intern tentará adicionar a string hello ao pool constante e retornar seu endereço no pool constante. Como existe uma string de hello no pool constante, o método estagiário retorna diretamente o endereço; Enquanto S1 já aponta para o pool constante durante o período de compilação, então S1 e S6 apontam para o mesmo endereço, o que é igual.
Neste ponto, podemos tirar três conclusões muito importantes:
Você deve prestar atenção ao comportamento durante o período de compilação para entender melhor o pool constante.
As constantes no pool constante de tempo de execução vêm basicamente do pool constante em cada arquivo de classe.
Quando o programa estiver em execução, a JVM não adicionará automaticamente constantes ao pool constante, a menos que adicione manualmente constantes ao pool constante (como chamar o método estagiário).
O acima envolve apenas pools constantes de cordas. De fato, existem piscinas constantes inteiras, piscinas constantes de ponto flutuante, etc., mas são semelhantes, mas piscinas constantes de tipos numéricos não podem ser adicionados manualmente. As constantes no pool constante são determinadas quando o programa é iniciado. Por exemplo, a faixa constante no pool constante inteiro é: -128 ~ 127. Somente números nesse intervalo podem ser usados para o pool constante.
prática
Dito tanta teoria, vamos tocar na verdadeira piscina constante.
Como mencionado anteriormente, existe um pool constante estático no arquivo de classe. Esse pool constante é gerado pelo compilador e é usado para armazenar literais no arquivo de origem Java (este artigo se concentra apenas em literais). Suponha que tenhamos o seguinte código Java:
String s = "oi";
Por conveniência, é tão simples, isso mesmo! Depois de compilar o código em um arquivo de classe, use o WINHEX para abrir o arquivo de classe de formato binário. Como mostrado na imagem:
Vamos explicar brevemente a estrutura do arquivo de classe. Os 4 bytes no início são o número mágico do arquivo de classe, que é usado para identificar isso como um arquivo de classe. Para ser franco, é o cabeçalho do arquivo, que é: ca fe ba.
Os próximos 4 bytes são o número da versão de Java, e o número da versão aqui é 34, porque o autor é compilado com o JDK8, e o número da versão corresponde ao nível da versão JDK. A versão superior pode ser compatível com a versão inferior, mas a versão inferior não pode executar a versão superior. Portanto, se um dia os leitores quiserem saber com que versão do JDK o arquivo de outras pessoas é compilado, você pode olhar para esses 4 bytes.
Em seguida é a entrada constante da piscina. O número de constantes constantes de constantes é identificado por 2 bytes na entrada. Neste exemplo, o valor é 00 1a. É traduzido em decimal e tem 26 anos, o que significa que existem 25 constantes. A constante 0ª é um valor especial, portanto, existem apenas 25 constantes.
A piscina constante armazena vários tipos de constantes. Todos eles têm seus próprios tipos e suas próprias especificações de armazenamento. Este artigo se concentra apenas em constantes de string. As constantes da string começam com 01 (1 byte) e, em seguida, registre o comprimento da string com 2 bytes e, em seguida, o conteúdo real da string. Nesse caso, é: 01 00 02 68 69.
Em seguida, vamos falar sobre o pool constante de tempo de execução. Como o pool constante de tempo de execução está na área do método, podemos definir o tamanho da área do método através dos parâmetros da JVM: -xx: PermSize, -xx: maxPermsize, limitando indiretamente o tamanho constante do pool.
Suponha que o parâmetro de inicialização da JVM seja: -xx: PermSize = 2m -xx: maxpermsize = 2m e, em seguida, execute o seguinte código:
// Mantenha as referências para evitar a lista automática de coleta de lixo <String> list = new ArrayList <String> (); int i = 0; while (true) {// Adicione manualmente list constante.add (String.valueof (i ++). Intern ());}O programa será lançado imediatamente: exceção no thread "main" java.lang.outOfMemoryError: exceção do espaço permgen. O espaço permgen é a área do método, o que é suficiente para indicar que o pool constante está na área do método.
No JDK8, a área do método foi removida e a área do Metaspace foi substituída. Portanto, precisamos usar o novo parâmetro JVM: -xx: maxmetaspacesize = 2m e ainda executar o código acima, arremessando: java.lang.outOfMemoryError: Exceção do metapacêutico. Da mesma forma, é explicado que o pool constante de tempo de execução é dividido na área de Metaspace. Para um conhecimento específico sobre a área do Metaspace, procure sozinho.
Todos os códigos deste artigo foram testados e aprovados no JDK7 e JDK8. Outras versões do JDK podem ter pequenas diferenças. Por favor, explore você mesmo.