Muitos métodos static, é bom ou ruim?

Pessoal, bom dia!

Tenho buscado muita informação sobre assuntos de funcionamento da jvm, gc, etc e fico cada vez mais confuso, ja que cada dia que passa descubro que sei quase nada de java. :?
Vamos a minha dúvida.

Sei que os métodos static são carregados no classloader e sei também que eles são mais rápidos, a minha dúvida é se uma aplicação grande tiver muitos métodos static em muitas classes o que realmente vai acontecer?
A aplicação será mais rápida por ter muitos métodos rápidos ou sera mais lenta por ter muitas informações carregadas e não usadas frequentemente?

Obrigado!

O que irá acontecer é de a aplicação utilizar muitos métodos estáticos é de se programar em estilo procedural, não tendo nada
de OO no código.

Se for uma aplicação bem pequena(tipo hello world) até pode ser mais rápido desenvolver, mas se for uma aplicação comercial séria, mesmo que pequena,
a manutenção disso se tornará um inferno de gambiarra. Assim, será um programa em java estilo C anos 70.

Ola johnny quest obrigado pela resposta, mas a minha pergunta seria sobre o desempenho, ignorando o fato da modularização das classes e perda da OO.

[quote=mauricioadl]Pessoal, bom dia!

Tenho buscado muita informação sobre assuntos de funcionamento da jvm, gc, etc e fico cada vez mais confuso, ja que cada dia que passa descubro que sei quase nada de java. :?
Vamos a minha dúvida.

Sei que os métodos static são carregados no classloader
[/quote]

Quem é carregado é a classe, não os métodos.

Não. Um método estático é igual de rápido que um não estático. Lembre-se que o compilador java já decidiu qual método será invocado.

Não.

A rapidez depende da jvm e do hardware e não se usa estático ou não-estático. Fora que usar muitos estaticos não é OOP e nos tempos modernos o uso descontrolado de de static é um anti-pattern. O static tem o seu papel para métodos da classe ( não do objeto) que são necessários em alguns padrões.

Em JVM modernas , sobretudo no hotstop criar e destruir objetos é quase grátis e não deve ser considerado um problema. Em java não vale fazer otimizações loucas porque isso engana a jvm que tem mecanismos de otimização dinamicos ( coisas que no C são comuns como iterar ao contrário, por exemplo) .

Deixe a JVM otimizar o codigo, ela é muito eficiente nisso. Mas para ela otimizar seu codigo deve ser bom, ter boa saude. O código é tão mais saudável quando menor o escopo das variáveis e mais OO for.

Uma das principais diferenças do C# em relação ao Java é que em Java todos os métodos que não são static são " virtuais " (ou seja, será chamado o método implementado pela classe do objeto, não o que está implementado pela classe que representa o tipo da variável.

Em C# você deve especificar tal comportamento explicitamente, já que o padrão é chamar o método implementado pela classe que representa o tipo da variável.

Isso, em tese, sempre seria mais lento (porque você precisaria acessar sempre um bloco de dados que em C++ se chama ‘vtable’), mas a JVM está avançada suficientemente hoje em dia para chamar diretamente o método correspondente à classe que representa o tipo da variável dependendo do grau de otimização que ela conseguiu alcançar ao executar seu programa, e essa ‘desvantagem’ do Java na prática não existe mais.

Ou seja, na prática a diferença entre um método “static” e outro normal é que em um método não static um parâmetro a mais é passado implicitamente (this), ou seja, é algo que não aumenta o tempo de execução de forma alguma.

Ou seja, deixar seu programa cheio de métodos “static” não vai melhorar o tempo de execução de forma alguma. É melhor usar “static” onde ele deve ser usado, não como uma forma ingênua de melhorar o desempenho.
Muitas vezes onde o tempo de execução “pega” é, por exemplo, em uma configuração errada do DNS :slight_smile:

Vou dar outro exemplo boboca.

O que é mais rápido (supondo que o log esteja desabilitado, que é o que deve ocorrer em produção) :
a) if (logger.isDebugEnabled()) { logger.debug("Logging in user " + user.getName() + " with id " + user.getId()); }
ou
b) logger.debug("Logging in user " + user.getName() + " with id " + user.getId());

Já vi gente que disse que a alternativa a) era mais lenta (porque tinha de ficar testando, e além disso um programa menor é mais rápido, não? ) e já vi gente que disse que tanto faz.

Entretanto, em Java a opção a) é que é mais rápida (se o log estiver desabilitado), porque mesmo que você não logue nada (que é o que ocorre na alternativa b) ) você ainda tem o trabalho de calcular os parâmetros.

Ou seja, na verdade não é tão simples dizer, só de olhar um programa, se ele vai ser mais rápido ou não.

Além disso, considere que:
a) Métodos estáticos são mais difíceis de usar em contextos de multiplas threads - o que pode ter um impacto significativamente ruim na performance;
b) Métodos estáticos não fazem polimorfismo - já que esse é um atributo da instância;

Finalmente, onde você anda lendo sobre performance?
Cuidado com os mitos, a internet está cheia deles.

Eles geralmente surgem do exagero de uma informação.

O que é mais lento num método estático em relação a um não estático (ou, mais precisamente, um método polimorfico, em relação a um não polimorfico) é a invocação, não a execução. Ou seja, é o tempo que o compilador leva entre a linha que chama o método, e a primeira linha desse mesmo método - em outras palavras, o tempo para “entrar” no método. E, em ambos os casos, esse tempo é extremamente pequeno, provavelmente, muito menor do que os comandos dentro do método levarão para executar.

Mesmo no caso do C++, onde a vtable não é otimizada em tempo de execução, culpar um método polimorfico pelo desempenho de execução só faz sentido em casos onde:
a) A aplicação é CPU Bound (ou seja, faz muitos cálculos e poucas operações de entrada/saída);
b) O método estático é uma das peças centrais do processamento, e está rodando num loop;
c) O tempo de execução do método estático é muito pequeno.

Aplicações matemáticas, como as de computação gráfica, meteorologia, ou bolsa de valores, podem vir a atingir esses três objetivos simultaneamente. Aplicações comerciais, raramente.

Recomendo fortemente que você leia os artigos do Brian Goetz, em especial, a série chamada Java theory and practice:
http://www.briangoetz.com/pubs.html

[quote=ViniGodoy]Métodos estáticos são mais difíceis de usar em contextos de multiplas threads - o que pode ter um impacto significativamente ruim na performance;
[/quote]

Uma vez eu resolvi criar um método estático para formatar datas de acordo com um determinado padrão fixo de um SimpleDateFormat. O único problema é que o SimpleDateFormat não é thread-safe. (Eu não tinha lido as letras miúdas). Após bater a cabeça depois de alguns erros esporádicos muito estranhos, eu acabei descobrindo que:
a) Isso tinha sido registrado no Bug DataBase da Sun
b) e que a Sun (hoje Oracle) não ia resolver isso.

O método continuou estático, mas tive de usar um ThreadLocal para criar uma instância do SimpleDateFormat para cada thread. Isso foi muito chato de descobrir, e é um exemplo de que tipos de problemas podem ter métodos estáticos.

Pessoal, muito obrigado a todos, foi de grande conhecimento.

entaglement, é bem comum ver esse método static de formatação de datas. Sincronizar esse método não resolveria o problema?

Resolver resolve, mas eu não gosto de sincronização porque sincronização = serialização, e se você serializar uma operação lenta (formatação de datas) ela pode se tornar um gargalo no sistema.

Dentro da medida do possível, deve-se evitar a sincronização, a menos que isso seja inevitável e você não tenha realmente uma boa alternativa (no meu caso, uma ThreadLocal resolvia bem meu problema, porque transformava o problema de sincronizar uma operação lenta (formatar uma data) no problema de sincronizar uma operação que em média é rápida (escolher no HashMap embutido no ThreadLocal o objeto correspondente à thread corrente).

Isso só vale se quando o método estático usa uma variável compartilhada (leia-se, externa ao método), não é Vini?

Tanto é que várias classes das versões mais antigas do JDK (como Hashtable, Vector e StringBuffer) têm sincronização implícita (se você olhar os fontes, há um monte de “synchronized” nessas classes) que foi removida nas versões mais modernas dessas classes (HashMap, ArrayList e StringBuilder).

Isso só vale se quando o método estático usa uma variável compartilhada (leia-se, externa ao método), não é Vini?[/quote]

Acredito que sim, devido ao escopo… ou estou errado ???
Como sempre evitei métodos estáticos, fiquei na duvida se este código pode apresentar problema:

public static String converterDataString(Date cal, String formato) throws CoreException {
		try {
			SimpleDateFormat sdf = new SimpleDateFormat(formato);
			String dataStr = sdf.format(cal);
			return dataStr;
		} catch (Exception ex) {
			throw new CoreException("Erro em converterDataString.", ex);
		}
	}

Pessoal, desculpe ficar “petulando” com duvidas idiotas. Por exemplo um método assim:

static Object outro = new ObjectQualquer();
public static ObjectQualquer fazAlgumaCoisa(Object parametro) {
// algumas operações 
return outro.fazAlqumaCoisa(parametro);
}

O exemplo é meio idiota, mas acho que vc vai entender.
Vamos supor que uma thread executa esse método e na linha 1 ela dorme e deixa a outra thread rodar, a outra executa o metodo tambem e encerra e volta para onde a primeira parou para continuar, o objeto outro teve sua informação alterada pela segunda thread e agora a informação dele ficou errada para a primeira que dormiu. o que vai acontecer? dar concurrent exception ou o valor vai retornar incorreto?

vlw

A resposta correta é: Não se sabe qual o valor do retorno.

Não vai atirar uma exceção por que a especificação do Java não fala nada sobre isso. Se quiser que aconteça uma exceção, você terá que implementá-la.

Sim, caso contrário, não há necessidade sequer de sincronizá-lo. As variáveis locais serão diferentes em cada thread, portanto, não se tratam da mesma região de memória, isto é, não há região crítica para se incomodar.

Agora, se você adota como prática usar métodos estáticos só pelo fato de “serem mais rápidos”, duvido muito que você não irá criar uma variável estática cedo ou tarde.

[quote=jmmenezes]

public static String converterDataString(Date cal, String formato) throws CoreException {
		try {
			SimpleDateFormat sdf = new SimpleDateFormat(formato);
			String dataStr = sdf.format(cal);
			return dataStr;
		} catch (Exception ex) {
			throw new CoreException("Erro em converterDataString.", ex);
		}
	}
[/code][/quote]

Nesse caso não há problema, pois o método só usa variáveis locais. Cada thread inicializará seu próprio conjunto de variáveis desse tipo, portanto, não há memória sendo compartilhada.
O problema ocorre em um método assim:

[code]public static final SimpleDateFormat FORMATO_PADRAO = new SimpleDateFormat("dd/MM/yyyy");

public static String converterDataString(Date cal) throws CoreException {
		try {
			String dataStr = FORMATO_PADRAO.format(cal);
			return dataStr;
		} catch (Exception ex) {
			throw new CoreException("Erro em converterDataString.", ex);
		}
	}

Se sincronizar esse método, você está criando um ponto onde todas as threads do sistema irão se enfileirar - não interessando que objeto estão percorrendo.

Por isso a alternativa de usar um threadLocal, que o entanglement falou. O threadLocal simplesmente cria uma cópia do FORMATO_PADRAO em cada thread. Assim, threads diferentes estarão percorrendo objetos diferentes.

Enfim, é ter que lembrar desse tipo de cuidado que significa “serem mais difíceis de serem usados em contexto multithread”.

Esqueci de mais um problema, mas relacionado a variáveis estáticas:
Variáveis estáticas nunca saem de escopo. Portanto, o que referenciam só será coletado pelo garbage collector se essas variáveis forem explicitamente atribuídas a null.

Se você tiver como prática criar muitos métodos estáticos, cedo ou tarde usará variáveis desse tipo para gerenciar seus módulos… e aí, complicou a questão de threads e de coleta de lixo.

Não é à toa que variáveis estáticas estão entre as principais causas de memory leaks.

Hum… lembrei de mais um detalhinho chato de static + synchronized.

Digamos que você, como eu, tenha o costume de organizar seus métodos estáticos utilitários em uma única classe. Se você tivesse feito algo como

public class Util {
    protected Util() { }
    public synchronized static String formatarData () { ... }
    private static DateFormat formatoData = ...;
    public synchronized static String formatarHora () { ... }
    private static DateFormat formatoHora = ...;
}

vai constatar que a execução de formatarData() e a execução de formatarHora() estão sendo mutuamente excludentes, o que não é o efeito desejado porque elas deveriam ser independentes entre si (afinal de contas, synchronized deve proteger apenas o objeto que não é thread-safe, ou seja, formatoData ou formatoHora. É que

    public synchronized static String formatarData() { ... }
    public synchronized static String formatarHora () { ... }

equivale, para fins de sincronização, a

    public static String formatarData() { 
        synchronized (Util.class) { .... }
    }
    public static String formatarHora() { 
        synchronized (Util.class) { .... }
    }

ou seja, eles estão sincronizando no mesmo objeto, quando na verdade deveriam sincronizar em objetos diferentes (respectivamente, formatoData e formatoHora).

Até aprender todas essas sutilezas é melhor evitar o uso de synchronized, a menos que seja completamente inevitável.

[quote=ViniGodoy]Esqueci de mais um problema, mas relacionado a variáveis estáticas:
Variáveis estáticas nunca saem de escopo. Portanto, o que referenciam só será coletado pelo garbage collector se essas variáveis forem explicitamente atribuídas a null.

Se você tiver como prática criar muitos métodos estáticos, cedo ou tarde usará variáveis desse tipo para gerenciar seus módulos… e aí, complicou a questão de threads e de coleta de lixo.

Não é à toa que variáveis estáticas estão entre as principais causas de memory leaks.[/quote]

É o tipo de coisa que só se deve usar quando tem certeza o que esta fazendo… mas esse assunto é longo…