Otimize os programas através das especificações do código Java, otimizam o uso da memória e evitam vazamento de memória
Os recursos disponíveis para a utilização de programas (Memory, CPU Time, Network Bandwidth etc.) são limitados. A otimização geralmente inclui dois aspectos: reduzir o tamanho do código e melhorar a eficiência de execução do código. Este artigo discute principalmente como melhorar a eficiência do código.
Nos programas Java, a maioria dos motivos dos problemas de desempenho não está no idioma Java, mas no próprio programa. É muito importante desenvolver bons hábitos de escrita de código, como aplicar corretamente e inteligentemente a classe java.lang.string e java.util.vector Class, que pode melhorar significativamente o desempenho do programa. Vamos analisar esse problema em detalhes abaixo.
1. Tente especificar que o modificador final da classe.
Na API Java Core, há muitos exemplos de aplicação final, como java.lang.string. A especificação final da classe String impede as pessoas de substituir o método Length (). Além disso, se uma classe for especificada como final, todos os métodos dessa classe serão finais. O compilador Java procurará oportunidades para incluir todos os métodos finais (isso está relacionado à implementação específica do compilador). Esse movimento pode melhorar o desempenho em uma média de 50%.
2. Tente reutilizar o objeto.
Especialmente ao usar objetos de string, o StringBuffer é usado quando ocorre concatenação da String. Como o sistema não apenas leva tempo para gerar objetos, também pode levar tempo para coletar e processar esses objetos no futuro. Portanto, gerar muitos objetos terá um grande impacto no desempenho do programa.
3. Tente usar variáveis locais.
Outras variáveis, como variáveis estáticas, variáveis de instância, etc., são criadas na pilha e são mais lentas. Além disso, as variáveis locais podem ser otimizadas ainda mais, dependendo do compilador/JVM específico. Consulte "Use variáveis de pilha sempre que possível".
4. Não repita a inicialização de variáveis <r /> por padrão, ao chamar o construtor de classe, o Java inicializará a variável para um determinado valor: todos os objetos são definidos como variáveis nulas e inteiras (byte, curta, int, longa) definidas Para 0, as variáveis flutuantes e duplas são definidas como 0,0 e os valores lógicos são definidos como falsos. Isso deve ser observado especialmente quando uma classe é derivada de outra, porque quando um objeto é criado com a nova palavra -chave, todos os construtores da cadeia do construtor são chamados automaticamente.
5. No desenvolvimento do sistema de aplicativos Java + Oracle, tente usar o formulário de maiúsculas em Java para reduzir a carga de análise do analisador Oracle.
6. Durante a programação do Java, tenha cuidado ao executar conexões de banco de dados e operações de streaming de E/S.
Como a operação desses objetos grandes causará grandes despesas gerais do sistema e, se você não tomar cuidado, isso levará a sérias conseqüências.
7. Como a JVM possui seu próprio mecanismo de GC, não requer muita consideração dos desenvolvedores do programa, o que reduz o ônus dos desenvolvedores em certa medida, mas também perde os perigos ocultos. No sistema .
A condição para a JVM reciclar o lixo é que o objeto não é referenciado; Portanto, é recomendável que o defina manualmente como NULL depois que o objeto for usado.
8. Ao usar o mecanismo de sincronização, tente usar a sincronização do método em vez da sincronização do bloco de código.
9. Minimize os cálculos repetidos de variáveis <br /> por exemplo: para (int i = 0; i <list.size; i ++) {
...
}
Deve ser substituído por:
for (int i = 0, int len = list.size (); i <len; i ++) {
...
}
10. Tente adotar a estratégia de carregamento preguiçosa, ou seja, comece a criar quando necessário.
Por exemplo: string str = "aaa";
if (i == 1) {
list.add (str);
}
Deve ser substituído por:
if (i == 1) {
String str = "aaa";
list.add (str);
}
11. Use anormalidades com cautela
A anormalidade não é boa para o desempenho. Para fazer uma exceção, você deve primeiro criar um novo objeto. O construtor da interface jogável chama o método local (nativo) chamado FillInstackTrace (), e o método FillInstackTrace () verifica a pilha e coleta informações de lixo. Enquanto uma exceção for lançada, a VM deve ajustar a pilha de chamadas porque um novo objeto é criado durante o processamento. Exceções só podem ser usadas para manuseio de erros e não devem ser usadas para controlar o fluxo do programa.
12. Não o use em um loop:
Tentar {
} pegar() {
}
Deve ser colocado na camada mais externa.
13. Uso de StringBuffer:
StringBuffer representa uma sequência variável e gravável.
Existem três métodos de construção:
StringBuffer ();
StringBuffer (tamanho int);
StringBuffer (String str);
O construtor mencionado aqui é StringBuffer (comprimento int) e o parâmetro de comprimento indica o número de caracteres que o StringBuffer atual pode conter. Você também pode usar o método EnsureCapacity (int Capacidade mínima) para definir sua capacidade após a criação do objeto StringBuffer. Primeiro, vamos dar uma olhada no comportamento padrão do StringBuffer e, em seguida, encontrar uma maneira melhor de melhorar o desempenho.
O StringBuffer mantém uma matriz de caracteres internamente. Quando o StringBuffer atingir sua capacidade máxima, aumentará sua capacidade para 2 vezes a capacidade atual e adiciona 2, ou seja (2*valor antigo +2). Se você usar o valor padrão, após a inicialização, adicione caracteres a ele. a 70 (2*34+2). Não importa o quê, desde que o StringBuffer atinja sua capacidade máxima, ele deve criar uma nova matriz de personagens e depois re-copiar os personagens antigos e novos. Portanto, não é errado sempre definir um valor de capacidade de inicialização razoável para o StringBuffer, que trará ganho de desempenho imediato. Isso mostra o papel de ajustar o processo de inicialização do StringBuffer. Portanto, usar um valor de capacidade adequado para inicializar um StringBuffer é sempre uma sugestão ideal.
14. Use a classe java java.util.Vector razoavelmente.
Simplificando, um vetor é uma variedade de instâncias java.lang.Object. O vetor é semelhante a uma matriz e seus elementos podem ser acessados através de um índice na forma de um número inteiro. No entanto, após a criação de um objeto do tipo vetorial, o tamanho do objeto pode ser expandido e reduzido de acordo com a adição ou exclusão de elementos. Por favor, considere o seguinte exemplo de adição de elementos ao vetor:
Objeto bj = new Object ();
Vetor V = novo vetor (100000);
for (int i = 0;
I <100000;
A menos que haja motivo absolutamente suficiente para exigir que novos elementos sejam inseridos na frente do vetor toda vez, o código acima é ruim para o desempenho. No construtor padrão, a capacidade de armazenamento inicial do vetor é de 10 elementos. A classe vetorial é como a classe StringBuffer de objeto. O snippet de código a seguir são ordens de magnitude mais rápida que o exemplo anterior:
Objeto bj = new Object ();
Vetor V = novo vetor (100000);
para (int i = 0; i <100000; i ++) {v.add (obj);
A mesma regra se aplica ao método Remow () da classe vetorial. Como cada elemento no vetor não pode conter um "espaço" entre cada elemento, a exclusão de qualquer outro elemento, exceto o último elemento, faz com que os elementos após o elemento excluído avance. Ou seja, excluir o último elemento de um vetor é várias vezes menos "sobrecarga" do que excluir o primeiro elemento.
Supondo que queremos remover todos os elementos do vetor anterior, podemos usar este código:
para (int i = 0; i <100000; i ++)
{
v.Remove (0);
}
No entanto, em comparação com o código a seguir, o código anterior é de ordens de magnitude mais lentamente:
para (int i = 0; i <100000; i ++)
{
v.Remove (v.Size ()-1);
}
A melhor maneira de excluir todos os elementos de um objeto V do vetor de tipo é:
v.RemovealLelements ();
Suponha que o objeto V do vetor de tipo contenha a sequência "Hello". Considere o código a seguir, que remove a string "Hello" deste vetor:
String s = "hello";
int i = v.indexOf (s);
if (i! = -1) v.Remove (s);
O código parece nada errado, mas também é ruim para o desempenho. Neste código, o método indexOf () pesquisa v na sequência para encontrar a string "hello" e o método Remover (s) também precisa pesquisar na mesma ordem. A versão aprimorada é:
String s = "hello";
int i = v.indexOf (s);
if (i! = -1) v.Remove (i);
Nesta versão, fornecemos diretamente a posição exata do índice do elemento a ser excluído no método Remow (), evitando assim a segunda pesquisa. Uma versão melhor é:
String S = "Hello";
Finalmente, vamos olhar para um trecho de código sobre a classe vetorial:
for (int i = 0; i ++; i <v.Length)
Se V contiver 100.000 elementos, este snippet de código chamará o método V.Size () 100.000 vezes. Embora o método de tamanho seja um método simples, ele ainda requer a sobrecarga de uma chamada de método, pelo menos a JVM precisa configurar e limpar o ambiente de pilha para ele. Aqui, o código dentro do loop for não modificará o tamanho do objeto V do tipo vetorial, de forma alguma, portanto o código acima é melhor reescrito no seguinte formulário:
int size = v.size ();
Embora essa seja uma mudança simples, ela ainda ganha desempenho. Afinal, todo ciclo de CPU é precioso.
15. Ao copiar uma grande quantidade de dados, use o comando System.arrayCopy ().
16. REFORTAÇÃO DO CÓDIGO: Aprimore a legibilidade do código .
Por exemplo:
classe pública ShopCart {Private List Carts;… public void add (item do objeto) {if (carts == null) {carts = new ArrayList ();} crts.add (item);} public void Remover (item do objeto) {se se (carrinhos. . 17. Crie uma instância de uma classe sem usar novas palavras -chave
Ao criar uma instância de uma classe com a nova palavra -chave, todos os construtores na cadeia do construtor serão chamados de automaticamente. Mas se um objeto implementar a interface clonável, podemos chamar seu método clone (). O método clone () não chama nenhum construtor de classe.
Ao usar o padrão de design, se você usar o modo de fábrica para criar um objeto, é muito simples usar o método clone () para criar uma nova instância do objeto. Por exemplo, a seguir é uma implementação típica do padrão de fábrica:
Crédito estático público getNewCredit () {
retornar novo crédito ();
}
O código aprimorado usa o método clone () da seguinte forma:
Crédito estático privado Basecredit = new Credit ();
Crédito estático público getNewCredit () {
retorno (crédito) basecredit.clone ();
}
A ideia acima também é muito útil para o processamento de matrizes.
18. Multiplicação e divisão
Considere o seguinte código:
para (val = 0; val <100000; val += 5) {
alterx = val * 8;
}
A substituição de multiplicação por operações de turno pode melhorar bastante o desempenho. Aqui está o código modificado:
para (val = 0; val <100000; val += 5) {
alterx = val << 3;
}
O código modificado não se multiplica mais por 8, mas, em vez disso, usa o deslocamento esquerdo equivalente de 3 bits, com 1 bit por turno esquerdo equivalente a multiplicar por 2. Consequentemente, a mudança certa pela operação de 1 bit é equivalente à divisão por 2. Vale ressaltar que, embora a operação de mudança seja rápida, pode dificultar o entendimento do código, por isso é melhor adicionar alguns comentários.
19. Feche sessões inúteis na página JSP.
Um mal -entendido comum é que a sessão é criada quando há acesso ao cliente. , use <> para fechar -o. Como a sessão consome recursos de memória, se você não planeja usar a sessão, feche -o em todos os JSPs.
Para páginas que não precisam rastrear o status da sessão, o fechamento de sessões criadas automaticamente pode economizar alguns recursos. Use a seguinte diretiva da página: <%@ página session = "false"%>
20. JDBC e E/S
Se um aplicativo precisar acessar um conjunto de dados em larga escala, considere usar a extração de blocos. Por padrão, o JDBC extrai 32 linhas de dados a cada vez. Por exemplo, suponha que desejemos atravessar um conjunto de registros de 5000 linhas, o JDBC deve ligar para o banco de dados 157 vezes antes de extrair todos os dados. Se o tamanho do bloco for alterado para 512, o número de chamadas para o banco de dados será reduzido para 10 vezes.
21. Uso de servlet e memória <br /> Muitos desenvolvedores economizam uma grande quantidade de informações nas sessões de usuário à vontade. Às vezes, os objetos armazenados na sessão não são reciclados pelo mecanismo de coleta de lixo no tempo. Do ponto de vista do desempenho, um sintoma típico é que o usuário sente que o sistema está desacelerando periodicamente, mas não pode atribuir o motivo a nenhum componente específico. Se você monitorar o espaço da pilha da JVM, ela se manifestará como flutuações anormais de uso da memória.
Existem duas maneiras principais de resolver esse tipo de problema de memória. O primeiro método é implementar a interface HTTPSessionBindingListener em todos os grãos com um escopo de sessão. Dessa maneira, desde que o método ValueUnbound () seja implementado, os recursos usados pelo feijão podem ser explicitamente liberados.
Outra maneira é invalidar a sessão o mais rápido possível. A maioria dos servidores de aplicativos tem a opção de definir o intervalo de invalidação da sessão. Além disso, o método setMaxinActiveInterval () da sessão também pode ser chamado programaticamente.
22. Use marcas de buffer
Alguns servidores de aplicativos adicionaram função de marcação de buffer para JSP. Por exemplo, o WebLogic Server da BEA suporta esse recurso desde a versão 6.0, e o projeto Symphony Open também suporta esse recurso. As tags de buffer JSP podem buffer os fragmentos de página e a página inteira. Quando a página JSP for executada, se o fragmento de destino já estiver no buffer, o código que gera o fragmento não precisará mais ser executado. O buffer de nível de página captura solicitações ao URL especificado e buffers toda a página de resultado. Esse recurso é extremamente útil para cestas de compras, catálogos e páginas domésticas do portal. Para tais aplicativos, o buffer de nível de página pode salvar os resultados da execução da página para solicitações subsequentes.
23. Escolha o mecanismo de citação certo
Em um sistema de aplicativos JSP típico, as peças de cabeçalho e rodapé são frequentemente extraídas e, em seguida, o cabeçalho e o rodapé são introduzidos conforme necessário. Atualmente, existem dois métodos principais para introduzir recursos externos nas páginas JSP: inclua diretivas e inclua ações.
Inclua Diretiva: por exemplo, < %@ incluir file = "Copyright.html" %>. Esta diretiva apresenta o recurso especificado no momento da compilação. Antes da compilação, a página com a diretiva incluir e o recurso especificado são mesclados em um arquivo. Os recursos externos referenciados são determinados no momento da compilação, o que é mais eficiente do que determinar os recursos em tempo de execução.
Inclua ação: por exemplo <jsp: incluir página = "Copyright.jsp" />. Esta ação apresenta o resultado gerado após a execução da página especificada. Como é concluído em tempo de execução, o controle dos resultados da saída é mais flexível. No entanto, é apenas econômico usar a ação quando o conteúdo cotado é alterado com frequência ou quando a página referenciada não pode ser determinada antes que a solicitação da página principal apareça.
24. Clear não é mais necessário sessões no tempo
Para limpar as sessões que não estão mais ativas, muitos servidores de aplicativos têm um tempo limite de sessão padrão, geralmente 30 minutos. Quando o servidor de aplicativos precisa salvar mais sessões, se a capacidade de memória for insuficiente, o sistema operacional transferirá parte dos dados da memória para o disco. Armazene no disco e pode até lançar uma exceção "fora da memória". Em sistemas em larga escala, as sessões de serialização são caras. Quando a sessão não é mais necessária, o método httpssession.Invalidate () deve ser chamado a tempo para limpar a sessão. O método httpSession.Invalidate () geralmente pode ser chamado na página de saída do aplicativo.
25. Não declare a matriz como: Public Static Final.
26. Discussão sobre a eficiência de travessia do hashmap
Muitas vezes, existem operações de travessia em pares de chave e valor no hashmap, e existem dois métodos: map <string, string []> paramap = novo
Hashmap <string, string []> (); ............ // o primeiro conjunto de loop <string> appfieldDefids = paramap.keyset (); ) ); String [] valores = entradas.getValue (); .......}
A primeira implementação é significativamente menos eficiente que a segunda implementação.
A análise é a seguinte set <string> AppFieldDefids = Paramap.keySet ();
O código é o seguinte:
Public Set <K> KeySet () {set <k> ks = keyset; retorna (ks! = null? ks: (keyset = new Keyset ()));} Classe privada Keyset estende o abstractSet <k> {public iterator <k > iterator () {return newkeyIterator ();} public int size () {return size;} public booleano (o)! = null;} public void clear () {hashmap.this.clear ();}}De fato, ele retorna um tecla de classe privada, que é herdada do AbstractSet e implementa a interface definida.
Vamos dar uma olhada na sintaxe de for/in loops
para (Declaração: Expressão)
declaração
Durante a fase de execução, é traduzida para as seguintes fórmulas
for (iterator <e> #i = (expressão) .iterator (); #i.hashNext ();) {
declaração = #i.Next ();
declaração
}
Portanto, Hashmap.KeySet (). Iterator () é chamado no primeiro para a declaração para (String AppFieldDefid: AppFieldDefids)
Este método chama NewKeyIterator ()
Iterator <k> newkeyIterator () {
retornar new keyIterator ();
}
classe privada KeyIterator estende hashIterator <k> {
public K Next () {
retornar nextEntry (). getKey ();
}
}
Portanto, no para, o iterador usado no segundo loop para (entrada <string, string []> entrada: paramap.entryset ()) é chamado da seguinte maneira.
tipo
A classe de entrada privada estende hashIterator <pp.entry <k, v >> {
public map.entry <k, v> next () {
retornar nextEntry ();
}
}
Nesse momento, o primeiro loop recebe a chave e o segundo loop recebe a eficiência de entrada do hashmap é que o segundo loop refletido no loop. Use o hashmap get (chave do objeto) para obter o valor do valor agora veja o método Get (chave do objeto) do hashmap
public v get (chave do objeto) {
Objeto k = MaskNull (chave);
int hash = hash (k);
int i = indexfor (hash, tabela.length);
Entrada <k, v> e = tabela;
while (true) {
if (e == null)
retornar nulo;
if (e.hash == hash && eq (k, e.key))
retornar e.value;
e = e.next;
}
}
De fato, é usar o valor de hash para obter a entrada correspondente novamente para comparar e obter o resultado.
No segundo loop, obtém o valor de entrada e, em seguida, pegue diretamente a chave e o valor, o que é mais eficiente que o primeiro loop. De fato, de acordo com o conceito de mapa, deve ser melhor usar o segundo loop.
27. Uso de Array (Array) e Arrylist
Array ([]): o mais eficiente; mas sua capacidade é fixa e não pode ser alterada dinamicamente;
Arraylist: a capacidade pode crescer dinamicamente;
Com base na eficiência e na verificação do tipo, a matriz deve ser usada o máximo possível.
Arraylist é uma versão complexa da matriz
A Arraylist encapsula uma matriz do tipo Objeto Método de matriz.
Quando a ArrayList armazena um objeto, as informações do tipo são descartadas e todos os objetos são bloqueados como objeto.
NOTA: O JDK5 adicionou suporte para genéricos, e a verificação do tipo pode ser executada ao usar o ArrayList.
Desse ponto de vista, a diferença entre Arraylist e Array se deve principalmente à eficiência do aumento da capacidade dinâmica.
28. Tente usar o HashMap e o Arraylist .
29. A diferença entre StringBuffer e StringBuilder:
java.lang.StringBuffer Sequência de caracteres mutável segura para roscas. Um buffer de string semelhante a uma string, mas não pode ser modificado.
StringBuilder. Comparado a esta classe, a classe java.lang.stringbuilder geralmente deve ser preferida porque suporta todas as mesmas operações, mas é mais rápida porque não executa a sincronização. Para melhor desempenho, a capacidade do StirngBuffer ou Stirngbuilder deve ser especificada o máximo possível. Obviamente, se a string que você opera não exceder 16 caracteres, você não precisará dela. No mesmo caso, o uso do StirngBuilder pode atingir apenas cerca de 10% a 15% de melhoria de desempenho em comparação com o uso do StringBuffer, mas corre o risco de insegurança com vários threading. Na programação modular real, o programador responsável por um determinado módulo pode não ser capaz de determinar claramente se o módulo será colocado em um ambiente multithread, então: a menos que você possa determinar que o gargalo do seu sistema está no StringBuffer e Verifique se o seu módulo não será executado no modo multithread, caso contrário, use StringBuffer.
Outros suplementos:
1. Objetos limpos que não são mais usados no tempo e definidos como nulos
2. Use palavras -chave finais, estáticas e outras o máximo possível
3. Use objetos tamponados o máximo possível
Como otimizar o código para criar o arquivo de origem Java e o arquivo de classe compilada menor
1 Tente usar a herança.
2 Abra as opções de otimização do compilador Java: javac -o Esta opção excluirá o número da linha no arquivo de classe e declarará alguns métodos de pequeno segmento privado, estático e final como método embutido chamadas de método
3 Extraia o código comum
4 Não inicialize grandes matrizes. Armazenado em uma matriz, você pode primeiro colocar esses dados em uma string e depois analisar a string em uma matriz durante o tempo de execução
5. Os objetos do tipo de data ocupam muito espaço.
Tipo longo e depois converta para o tipo de data quando usado
6. Tente usar nomes curtos para nomes de classe, nomes de métodos e nomes de variáveis.
7 Defina variáveis do tipo final estático na interface
8 Se operações aritméticas puderem ser usadas para o movimento esquerdo / direito, não use * e / operações.
2. Não inicialize variáveis duas vezes
O Java inicializa a variável para um valor conhecido por padrão chamando um construtor de classe exclusivo. Todos os objetos são definidos como números inteiros nulos (byte, curto, int, longo) são definidos como 0, flutuar e duplo são definidos como 0,0, e as variáveis booleanas são definidas como falsas. Isso é especialmente importante para as classes que se estendem de outras classes, assim como todas as séries de construtores são chamadas automaticamente ao criar um objeto usando uma nova palavra -chave.
3. Faça a classe final sempre que possível
As classes marcadas finais não podem ser estendidas. Existem muitos exemplos dessa tecnologia na API Java Core, como java.lang.string. Marcar a classe String como final impede que os desenvolvedores criem métodos de comprimento que eles se implementam.
Para colocá -lo mais profundamente, se a classe for final, todos os métodos da classe serão finais. O compilador Java pode embrulhar todos os métodos (isso depende da implementação do compilador). Nos meus testes, vi um aumento médio no desempenho em 50%.
9. A exceção é lançada onde precisa ser jogado.
tente {algum.method1 (); (Method2Exception e) {// Handle Exception 2} tente {algum.method3 (); O código que foi baixado é mais fácil de ser otimizado pelo compilador
tente {algum.method1 (); Catch (Method3Exception e) {// Handle Exceção 3}
10. otimização de loop
Substituir…
for (int i = 0; i <collection.size (); i ++) {
...
}
com…
for (int i = 0, n = collection.size (); i <n; i ++) {
...
}
5. No desenvolvimento do sistema de aplicativos Java + Oracle, tente usar instruções SQL incorporadas em java para reduzir a carga de análise do analisador Oracle.
10. Tente adotar a estratégia de carregamento preguiçosa, ou seja, comece a criar quando necessário.
Por exemplo: string str = "aaa";
if (i == 1) {
list.add (str);
}
Deve ser substituído por:
if (i == 1) {
String str = "aaa";
list.add (str);
}
12. Não o use em um loop:
Tentar {
} pegar() {
}
Deve ser colocado na camada mais externa
O acima é tudo sobre este artigo.
Reserve algum tempo para compartilhar o artigo com seus amigos ou deixar um comentário. Agradecemos sinceramente o seu apoio!