Prefácio
Isso está bastante ocioso durante esse período, então olhei para o código -fonte do JDK. Um engenheiro geral de desenvolvimento sênior pode melhorar a si mesmo lendo algum código -fonte. Este artigo resume algumas "pequenas dicas" no código -fonte do JDK e as compartilha para sua referência e aprendizado. Não vou dizer muito abaixo, vamos dar uma olhada na introdução detalhada juntos.
1 i ++ vs i--
Linha 985 do código -fonte da string, no método igual
while (n-! = 0) {if (v1 [i]! = v2 [i]) retorna false; i ++; }Este código é usado para julgar se a string é igual, mas há uma coisa estranha que usa i-! = 0 para fazer julgamentos. Normalmente não usamos i ++? Por que usar eu-? E o número de ciclos é o mesmo. O motivo é que haverá mais uma instrução após a compilação:
I-- A operação em si afetará o CPSR (registro de status do programa atual). Os sinalizadores comuns para CPSR são n (resultado é negativo), z (resultado é 0), c (transporte) e O (estouro). i> 0, pode ser julgado diretamente pela bandeira z.
A operação de I ++ também afetará o CPSR (registro de status do programa atual), mas afetará apenas o sinalizador O (com estouro), o que não ajudará no julgamento de i <n. Portanto, é necessária uma instrução de comparação adicional, o que significa que mais uma instrução deve ser executada para cada loop.
Simplificando, em comparação com 0, haverá menos uma instrução. Portanto, reciclagem I--, de ponta, atmosférica e de ponta.
2 variáveis de membros vs variáveis locais
O código -fonte do JDK quase usa uma variável local para aceitar variáveis de membros em qualquer método, por exemplo
public int compareto (string anotherstring) {int len1 = value.length; int len2 = anototherstring.value.length;Como as variáveis locais são inicializadas na pilha de threads do método, enquanto as variáveis de membros são inicializadas na memória da pilha, obviamente o primeiro é mais rápido, então tentamos evitar o uso de variáveis de membro diretamente no método, mas, em vez disso, usam variáveis locais.
3 Carregando deliberadamente nos registros e coloque operações demoradas fora da fechadura
No concorrente, a operação do segmento de bloqueio é muito interessante. Não é uma trava direta, mas é semelhante a uma trava de spin. Ele tenta repetidamente adquirir a fechadura. Durante o processo de aquisição do bloqueio, ele atravessará a lista vinculada, para que os dados sejam carregados primeiro no cache do registro, evitando a conveniência no processo de bloqueio. Ao mesmo tempo, a operação de gerar novos objetos também é colocada fora da fechadura para evitar operações demoradas na trava
Final V Put (Key K, int hash, V Valor, Boolean Onlyifabsent) { /** Antes de escrever para este segmento, você precisa obter o bloqueio exclusivo do segmento primeiro. Não é para forçar o bloqueio (), mas tentar*/ hashentry <k, v> node = trylock ()? nulo: scanndlockForput (chave, hash, valor); ScanndlockForput () Código -fonte
Hashentry privado <k, v> scanndlockForput (K -Key, int hash, V Value) {hashentry <k, v> primeiro = EntryForHash (this, hash); Hashentry <k, v> e = primeiro; Hashentry <k, v> node = null; Int Bettries = -1; // negativo ao localizar o nó // loop para obter trava enquanto (! Trylock ()) {hashentry <k, v> f; // Para recrutar primeiro abaixo se (recuperar <0) {if (e == null) {if (node == null) // cria especulativamente o nó // o bit de hash não tem valor, crie um novo objeto e não precisa ir ao método de put () para criar um novo nó = novo hashentry <k, v> (hash; tentativas = 0; } // A chave de posição do hash é a mesma, degenerando em uma bloqueio de spin sen if (key.equals (e.key)) tentativas = 0; else // as tentativas podem ler automaticamente a lista vinculada no cache e = e.next; } // Quando experimentas> 0, torna -se um bloqueio de spin. Obviamente, se o número de tentativas exceder o max_scan_retries (núcleo único 1 multi-núcleo 64), não o pegue, entre na fila de bloqueio e aguarde o bloqueio // bloqueio () é o método de bloqueio até que ele retorne após obter o bloqueio, caso contrário, se pendurará (++ Recompitados> max_scan_retries) {, caso contrário, se pendurará (++ Reconfortos> max_scan_retries). quebrar; } else if ((tentativas & 1) == 0 && // Há um grande problema no momento, ou seja, os novos elementos entram na lista vinculada e se tornam um novo cabeçalho //, portanto, a estratégia aqui é que é equivalente a passar pelo método scanNAndlockForput novamente (F = EntryFhash (this Hash))! // re -travessia se a entrada alterou as tentativas = -1; }} retornar node;} 4 Você pode usá -lo primeiro para determinar a igualdade do objeto ==
Ao julgar se os objetos são iguais, você pode usar == primeiro, porque == compara diretamente os endereços, o que é muito rápido, enquanto iguais compararão os valores mais objeto, que são relativamente lentos. Portanto, se possível, você pode usar a == b || a.quals (b) para comparar se os objetos são iguais.
5 sobre transitório
O transitório é usado para evitar a serialização, mas a matriz interna no código -fonte do hashmap é definida como transitória.
/*** A tabela, redimensionada conforme necessário. O comprimento deve sempre ser um poder de dois. */ entrada transitória <k, v> [] tabela = (entrada <k, v> []) emailt_table;
Então os pares de valor-chave não não podem ser serializados? Não é impossível transmitir usando o hashmap na rede? De fato, não é.
Java eficaz 2º, Item75, Josué mencionou:
Por exemplo, considere o caso de uma tabela de hash. O físico
Representação é uma sequência de baldes de hash contendo valor-chave
entradas. O balde em que uma entrada reside é uma função do hash
código de sua chave, que não é, em geral, garantido como o mesmo
Da implementação da JVM à implementação da JVM. Na verdade, não é nem mesmo
Garantido ser o mesmo da corrida para a corrida. Portanto, aceitando o
A forma serializada padrão para uma tabela de hash constituiria um grave
erro. Serializando e desserializando a tabela de hash pode produzir um
objeto cujos invariantes eram seriamente corruptos.
Como entender? Dê uma olhada no hashmap.get ()/put () para saber que o mapa de leitura e escrita é baseado no object.hashcode () para determinar qual bucket para ler/gravar. Object.HashCode () é um método nativo, que pode ser diferente em diferentes JVMs.
Por exemplo, salve uma entrada no hashmap, a chave é a string "String". No primeiro programa Java, o HashCode () de "String" é 1 e o número 1 do balde é armazenado; No segundo programa Java, o HashCode () de "String" pode ser 2 e o número 2 do balde é armazenado. Se a serialização padrão for usada (a tabela de entrada [] não requer transitório), depois que este hashmap importa o segundo programa Java por meio de serialização do primeiro programa Java, sua distribuição de memória é a mesma, o que está errado.
Por exemplo, se você salvar uma entrada de par de valores-chave no hashmap, key = "Fang Laosi", no primeiro programa Java, o hashcode () de "Fang Laosi" é 1 e a tabela [1] é armazenada. OK, agora ele é passado para outro programa JVM, o HashCode () de "Fang Laosi" pode ser 2, então você vai para a tabela [2] para obtê -lo e o resultado não existe.
O readObject e o WriteObject atuais do Hashmap são usados para produzir conteúdo de saída/entrada e regenerar o hashmap.
6 Não use char
O CHAR é codificado no UTF-16 em Java e é de 2 bytes e 2 bytes não podem representar todos os caracteres. Os 2 bytes são chamados BMP, e o outro é chamado de alto substituto e baixo substituto para formar um caractere de 4 bytes representado pelo escriba. Por exemplo, indexof no código -fonte da string:
// Aqui está um INT para aceitar um char para facilitar o julgamento do intervalo Public Int Indexof (int ch, int a partir deindex) {final int max = value.Length; if (fromIndex <0) {fromIndex = 0; } else if (fromIndex> = max) {// Nota: FromIndex pode estar próximo a -1 >>> 1. retornar -1; } // No intervalo BMP if (CH <caractere.min_supplementary_code_point) {// lide com a maioria dos casos aqui (CH é um ponto de código BMP ou A // Valor negativo (ponto de código inválido)) Final char [] valor = this.value; for (int i = fromIndex; i <max; i ++) {if (value [i] == ch) {return i; }} retornar -1; } else {// Caso contrário, vá para o método de julgamento de quatro bytes Retorno IndexOfSupplementário (CH, FromIndex); }}Portanto, o char de Java só pode representar os caracteres parciais do BMP no UTF16. Para os conjuntos parciais de personagens parciais de CJK (China, Japão e Coréia do Sul), eles não podem ser expressos.
Por exemplo, o char não pode ser representado, exceto pela parte ext-A na figura abaixo.
Além disso, há outro ditado de que você deve usar o char. Não use string para a senha. A string é uma constante (ou seja, não pode ser alterada após a criação) e será salva no pool constante. Se outros processos puderem despejar a memória do processo, a senha será vazada à medida que o pool constante for despejado e o char [] poderá escrever outras informações para alterar, o que significa que reduzirá o risco de vazar a senha.
Mas eu pessoalmente acho que você pode despejar memória. Um char pode ser capaz de evitá -lo? A menos que a corda não seja reciclada no pool constante e seja lida diretamente do pool constante por outros threads, provavelmente é muito raro.
Resumir
O acima é o conteúdo inteiro deste artigo. Espero que o conteúdo deste artigo tenha certo valor de referência para o estudo ou trabalho de todos. Se você tiver alguma dúvida, pode deixar uma mensagem para se comunicar. Obrigado pelo seu apoio ao wulin.com.