Foi-me dito por um colega que na criação de objetos Java é a operação mais cara que você pode executar. Portanto, só posso concluir a criação do menor número possível de objetos.
Isso parece derrotar o objetivo da programação orientada a objetos. Se não estamos criando objetos, estamos apenas escrevendo um estilo longo de classe C, para otimização?
Respostas:
Seu colega não tem idéia do que está falando.
Sua operação mais cara seria ouvi-los . Eles desperdiçaram seu tempo desviando-o de informações desatualizadas há mais de uma década (na data original em que essa resposta foi postada) , além de você ter que gastar algum tempo postando aqui e pesquisando na Internet para a verdade.
Espero que eles estejam apenas regurgitando algo que ouviram ou leram de mais de uma década atrás e não sabem nada melhor. Eu aceitaria qualquer outra coisa que eles dissessem como suspeita, essa deve ser uma falácia bem conhecida de qualquer um que se mantenha atualizado de qualquer maneira.
Tudo é um Objeto (exceto
primitives
)Tudo que não sejam primitivos (
int, long, double
etc) são objetos em Java. Não há como evitar a criação de objetos em Java.A criação de objetos em Java devido a suas estratégias de alocação de memória é mais rápida que o C ++ na maioria dos casos e, para todos os fins práticos, em comparação com tudo o mais na JVM, pode ser considerada "livre" .
No início dos anos 90, as implementações da JVM na década de 90 tinham um pouco de desempenho na alocação real de objetos. Este não é o caso desde pelo menos 2005.
Se você ajustar
-Xms
para suportar toda a memória necessária para que seu aplicativo seja executado corretamente, o GC talvez nunca precise executar e varrer a maior parte do lixo nas implementações modernas do GC, os programas de curta duração talvez nunca tenham o GC.Ele não tenta maximizar o espaço livre, que é um arenque vermelho de qualquer maneira, maximiza o desempenho do tempo de execução. Se isso significa que o JVM Heap é quase 100% alocado o tempo todo, que assim seja. A memória heap livre da JVM não fornece nada apenas para você ficar sentado ali mesmo.
Há um equívoco de que o GC libere memória de volta para o resto do sistema de uma maneira útil, isso é completamente falso!
O heap da JVM não aumenta e diminui, para que o restante do sistema seja afetado positivamente pela memória livre no heap da JVM .
-Xms
aloca TUDO o que é especificado na inicialização e sua heurística é nunca realmente liberar parte dessa memória de volta ao sistema operacional para ser compartilhada com outros processos do sistema operacional até que a instância da JVM saia completamente.-Xms=1GB -Xmx=1GB
aloca 1 GB de RAM, independentemente de quantos objetos são realmente criados a qualquer momento. Existem algumas configurações que permitem que as porcentagens da memória heap sejam liberadas, mas para todos os propósitos práticos a JVM nunca é capaz de liberar o suficiente dessa memória para que isso aconteça.portanto, nenhum outro processo pode recuperar essa memória; portanto, o resto do sistema também não se beneficia da liberação da JVM Heap. Uma solicitação de proposta foi "aceita" em 29 de novembro de 2006, mas nada foi feito a respeito. Esse é um comportamento que não é considerado uma preocupação por ninguém de autoridade.Há um equívoco de que a criação de muitos objetos pequenos e de curta duração faz com que a JVM faça uma pausa por longos períodos de tempo, agora isso também é falso
Os algoritmos atuais do GC são realmente otimizados para criar muitos objetos pequenos de vida curta, que é basicamente a heurística de 99% para objetos Java em todos os programas. Tentativas no pool de objetos realmente fazem com que a JVM tenha um desempenho pior na maioria dos casos.
Os únicos objetos que precisam de pool hoje são objetos que se referem a recursos finitos externos à JVM; Soquetes, arquivos, conexões com o banco de dados, etc., e podem ser reutilizados. Objetos regulares não podem ser agrupados no mesmo sentido que em idiomas que permitem acesso direto aos locais da memória. O cache de objetos é um conceito diferente e pode ou não ser o que algumas pessoas chamam de pooling ingenuamente , os dois conceitos não são a mesma coisa e não devem ser confundidos.
Os algoritmos modernos do GC não têm esse problema porque não se desalocam dentro de um cronograma, se desalocam quando a memória livre é necessária em uma determinada geração. Se o heap for grande o suficiente, não haverá desalocações por tempo suficiente para causar pausas.
Linguagens dinâmicas orientadas a objetos estão superando C mesmo hoje em dia nos testes sensíveis à computação.
fonte
_alloca
amortizado.new
palavra - chave em um local de ponto de acesso. Eu já vi pessoas usaremnew ImageIcon(Image)
opaint()
método de objetos Swing, o que é bastante caro e estava tornando a interface do usuário inteira muito lenta. Portanto, não é uma resposta em preto e branco, pense antes de usar emnew
algum lugar.Conclusão: não comprometa seu design para criar atalhos na criação de objetos. Evite criar objetos desnecessariamente. Se sensato, projete para evitar operações redundantes (de qualquer tipo).
Ao contrário da maioria das respostas - sim, a alocação de objetos TEM um custo associado. É um custo baixo, mas você deve evitar criar objetos desnecessários. O mesmo que você deve evitar qualquer coisa desnecessária no seu código. Gráficos de objetos grandes tornam o GC mais lento, implicando em tempos de execução mais longos, pois você provavelmente terá mais chamadas de método, acionará mais falhas de cache da CPU e aumentará a probabilidade de seu processo ser trocado para o disco em casos de pouca RAM.
Antes que alguém se queixa de que esse é um caso de extrema importância, criei um perfil de aplicativos que, antes de otimizar, criavam mais de 20 MB de objetos para processar ~ 50 linhas de dados. Isso é bom no teste, até você escalar até cem solicitações por minuto e de repente criar 2 GB de dados por minuto. Se você quiser fazer 20 reqs / s, crie 400 MB de objetos e jogue-os fora. 20 reqs / s é pequeno para um servidor decente.
fonte
while(something) { byte[] buffer = new byte[10240]; ... readIntoBuffer(buffer); ...
que pode ser um desperdício em comparação com, por exemplobyte[] buffer = new byte[10240]; while(something) { ... readIntoBuffer(buffer); ...
.Na verdade, devido às estratégias de gerenciamento de memória que a linguagem Java (ou qualquer outra linguagem gerenciada) possibilita, a criação de objetos é pouco mais do que incrementar um ponteiro em um bloco de memória chamado geração jovem. É muito mais rápido que C, onde é necessário fazer uma busca por memória livre.
A outra parte do custo é a destruição de objetos, mas é difícil comparar com C. O custo de uma coleção é baseado na quantidade de objetos salvos a longo prazo, mas a frequência das coleções é baseada na quantidade de objetos criados ... em no final, ainda é muito mais rápido que o gerenciamento de memória no estilo C.
fonte
Point
objeto pode caber em 2 registros de uso geral).Outros pôsteres apontaram corretamente que a criação de objetos é extremamente rápida em Java e que você não deve se preocupar com isso em nenhum aplicativo Java normal.
Existem algumas situações muito especiais em que é uma boa ideia evitar a criação de objetos.
fonte
Há um núcleo de verdade no que seu colega está dizendo. Eu respeitosamente sugiro que o problema com a criação de objetos seja realmente a coleta de lixo . Em C ++, o programador pode controlar com precisão como a memória é desalocada. O programa pode acumular petróleo pelo tempo que desejar. Além disso, o programa C ++ pode descartar o crud usando um thread diferente daquele que o criou. Portanto, o encadeamento atualmente funcionando nunca precisa parar para limpar.
Por outro lado, a Java Virtual Machine (JVM) periodicamente interrompe seu código para recuperar a memória não utilizada. A maioria dos desenvolvedores de Java nunca percebe essa pausa, pois geralmente é pouco frequente e muito curta. Quanto mais bruto você acumular ou mais restrita sua JVM, mais frequentes serão essas pausas. Você pode usar ferramentas como o VisualVM para visualizar esse processo.
Nas versões recentes do Java, o algoritmo de coleta de lixo (GC) pode ser ajustado . Como regra geral, quanto menor você desejar fazer as pausas, mais cara será a sobrecarga na máquina virtual (ou seja, CPU e memória gastas coordenando o processo do GC).
Quando isso pode importar? Sempre que você se preocupa com taxas de resposta consistentes em menos de milissegundos, se preocupa com o GC. Os sistemas de negociação automatizados escritos em Java ajustam fortemente a JVM para minimizar as pausas. As empresas que de outra forma gravariam Java recorrem ao C ++ em situações em que os sistemas precisam ser altamente responsivos o tempo todo.
Para o registro, eu não tolero a evitação de objetos em geral! Padrão para programação orientada a objetos. Ajuste essa abordagem somente se o GC estiver no seu caminho e somente depois de tentar ajustar a JVM para pausar por menos tempo. Um bom livro sobre ajuste de desempenho Java é o Java Performance, de Charlie Hunt e Binu John.
fonte
Há um caso em que você pode ser desencorajado de criar muitos objetos em Java devido à sobrecarga - projetando para o desempenho na plataforma Android
Fora isso, as respostas acima são verdadeiras.
fonte
o GC é ajustado para muitos objetos de vida curta
Dito isto, se você pode reduzir trivialmente a alocação de objetos, deve
Um exemplo seria construir uma string em um loop, a maneira ingênua seria
que cria um novo
String
objeto em cada+=
operação (mais umStringBuilder
e o novo conjunto de caracteres subjacente)você pode reescrever isso facilmente em:
esse padrão (resultado imutável e um valor intermediário local mutável) também pode ser aplicado a outras coisas
mas fora isso, você deve criar um perfilador para encontrar o gargalo real em vez de perseguir fantasmas
fonte
StringBuilder
abordagem é a pré-size oStringBuilder
modo que ele nunca tem que realocar a matriz subjacente com oStringBuilder(int)
construtor. Isso o torna uma alocação única , em vez de1+N
alocações.Joshua Bloch (um dos criadores da plataforma Java) escreveu em seu livro Effective Java in 2001:
fonte
Isso realmente depende da aplicação específica, por isso é realmente difícil de dizer em geral. No entanto, eu ficaria surpreso se a criação de objetos fosse realmente um gargalo de desempenho em um aplicativo. Mesmo que sejam lentos, o benefício no estilo do código provavelmente superará o desempenho (a menos que seja realmente perceptível para o usuário)
De qualquer forma, você não deve nem se preocupar com essas coisas até criar um perfil de seu código para determinar os gargalos de desempenho reais em vez de adivinhar. Até lá, você deve fazer o que for melhor para a legibilidade do código, não o desempenho.
fonte
Eu acho que seu colega deve ter dito da perspectiva da criação desnecessária de objetos. Quero dizer, se você estiver criando o mesmo objeto com frequência, é melhor compartilhar esse objeto. Mesmo nos casos em que a criação de objetos é complexa e ocupa mais memória, convém clonar esse objeto e evitar a criação desse processo complexo de criação de objetos (mas isso depende de seus requisitos). Eu acho que a afirmação "A criação de objetos é cara" deve ser tomada em contexto.
No que diz respeito aos requisitos de memória da JVM, aguarde o Java 8, você nem precisa especificar -Xmx, as configurações de metaespaço cuidarão da necessidade de memória da JVM e ela crescerá automaticamente.
fonte
Há mais para criar uma classe do que alocar memória. Também há inicialização, não sei por que essa parte não é coberta por todas as respostas. As classes normais contêm algumas variáveis e fazem alguma forma de inicialização, e isso não é gratuito. Dependendo da classe, ela pode ler arquivos ou executar qualquer outro número de operações lentas.
Portanto, considere o que o construtor da classe faz, antes de descobrir se é gratuito ou não.
fonte
O GC de Java é realmente muito otimizado em termos de criação de muitos objetos rapidamente, de maneira "rápida". Pelo que entendi, eles usam um alocador seqüencial (alocador O (1) mais rápido e mais simples para solicitações de tamanho variável) para esse tipo de "ciclo de burst" em um espaço de memória que eles chamam de "espaço Eden", e somente se os objetos persistirem depois de um ciclo de GC, eles são movidos para um local onde o GC pode coletá-los um a um.
Com isso dito, se o seu desempenho precisa ser crítico o suficiente (como medido com requisitos reais do usuário final), os objetos carregam uma sobrecarga, mas eu não pensaria muito nisso em termos de criação / alocação. Tem mais a ver com a localidade de referência e o tamanho adicional de todos os objetos em Java, conforme necessário para suportar conceitos como reflexão e envio dinâmico (
Float
é maior quefloat
, geralmente algo 4 vezes maior em 64 bits com seus requisitos de alinhamento e um matriz deFloat
não é necessariamente garantida para ser armazenada contígua do que eu entendo).Uma das coisas mais atraentes que já vi desenvolvidas em Java que me fizeram considerá-lo um contendor pesado no meu campo (VFX) foi um rastreador de caminho padrão interativo e multithread (sem usar cache de irradiância ou BDPT ou MLS ou qualquer outra coisa) em a CPU fornece visualizações em tempo real que convergiram rapidamente para uma imagem sem ruído. Eu trabalhei com profissionais em C ++ dedicando suas carreiras a essas coisas com criadores de perfil sofisticados em mãos que tiveram dificuldade em fazer isso.
Mas espiei o código-fonte e, embora ele usasse muitos objetos a um custo trivial, as partes mais críticas do traçador de caminhos (BVH e triângulos e materiais) evitavam objetos de maneira muito clara e deliberada em favor de grandes matrizes de tipos primitivos ( principalmente
float[]
eint[]
), o que o fez usar muito menos memória e localidade espacial garantida para passar de umfloat
na matriz para o próximo. Eu não acho muito especulativo pensar que, se o autor usasse tipos de caixas comoFloat
lá, teria um custo bastante alto para seu desempenho. Mas estamos falando da parte mais absolutamente crítica desse mecanismo, e tenho certeza, considerando a habilidade com que o desenvolvedor o otimizou, que ele mediu isso e aplicou essa otimização de maneira muito criteriosa, pois ele usava objetos em qualquer lugar com custo trivial para o seu impressionante rastreador em tempo real.Mesmo em um campo tão crítico quanto o meu, você não escreverá produtos eficientes se forçar em locais que não importam. Eu diria até que os campos mais críticos para o desempenho podem exigir ainda mais a produtividade, já que precisamos de todo o tempo extra possível para ajustar os pontos críticos que realmente importam, sem desperdiçar tanto tempo com coisas que não o fazem. . Como no exemplo do traçador de caminhos acima, o autor habilmente e judiciosamente aplicou essas otimizações apenas aos lugares que realmente, realmente importavam, e provavelmente em retrospectiva após a medição, e ainda usavam objetos em qualquer lugar.
fonte
float[]
vs.Float[]
, em que o processamento de um milhão do primeiro sequencialmente pode ser relativamente mais rápido que o segundo.Como as pessoas disseram, a criação de objetos não é um grande custo em Java (mas aposto que é maior que a maioria das operações simples, como adicionar etc) e você não deve evitá-lo demais.
Dito isso, ainda é um custo e, às vezes, você pode se livrar do maior número possível de objetos. Mas somente após a criação de perfil mostrou que isso é um problema.
Aqui está uma ótima apresentação sobre o tema: https://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf
fonte
Eu fiz uma rápida marca de microbench sobre isso e forneci fontes completas no github. Minha conclusão é que se a criação de objetos é cara ou não, não é o problema, mas a criação contínua de objetos com a noção de que o GC cuidará de tudo, pois você fará com que seu aplicativo inicie o processo do GC mais cedo. O GC é um processo muito caro e é melhor evitá-lo sempre que possível e não tentar pressioná-lo.
fonte