Duvida com tipo de acesso static

18 respostas
josimarsis

Olá galera gostaria de saber qual a melhor maneira de se trabalhar com metodos estáticos segue as duas situações abaixo.

//Classe A

private FrmConsulta consultar = null;
if(consultar == null){
   //getInstance() metodo estatico
   consultar = new FrmConsulta.getInstance();
}
consultar.consulta(String parametro);
//Classe A

FrmConsulta.getInstance().consulta(String parametro);

Andei lendo em livros e no google porém, não consegui entender qual a maneira ideal, qual consome menos memoria. Na minha maneira de pensar usando a primeira maneira eu estaria criando um objeto FrmConsulta em cada tela que eu fizesse uma consulta e como ele é static ficaria alocado na memoria.

18 Respostas

J-Chist

Seu método getInstance é estático, mas o objeto que ele cria também é estático? Um método static não cria objetos estáticos por definição. Ele somente é um método que pode ser invocado sem que você precise de uma instância da classe que o declara.

Pro seu objeto ser estático, no exemplo (já que não tem o código do getInstance() ) :

private static FrmConsulta consultar = null; if(consultar == null){ //getInstance() metodo estatico consultar = new FrmConsulta.getInstance(); } consultar.consulta(String parametro);

Se o objeto é estático, ele pode ser compartilhado por todas as instâncias ativas de uma classe e também pode ser acessado sem que seja necessário criar uma instância dessa classe (Classe.consultar, por exemplo), mas só vai ficar alocado em memória enquanto tiver valor diferente de nulo, senão ele é removido pelo garbage collector como qualquer outro objeto. Geralmente é preciso liberar explicitamente um objeto estático ( = null) para que ele possa ser coletado pelo GC, por isso algumas pessoas dizem que o uso desse tipo de objeto pode ocasionar um maior consumo de memória.

Mas você está desenvolvendo para qual plataforma? A menos que seja JME não se justifica tanta preocupação com o consumo de memória justamente por causa do garbage collector, que se preocupa com essas coisas pra você. Espero ter ajudado.

josimarsis

deixa eu ver se entendi. Então a melhor maneira seria usar a segunda opção onde eu acesso o objeto diretamente certo?

J-Chist

Isso depende de qual é sua intenção. Na segunda opção você também está criando um objeto. A única diferença dela para a primeira é que você cria um objeto sem uma referência direta (sem um nome para chamá-lo). Já me falaram que essa abordagem consome mais memória, mas eu não sei o motivo. Penso que porque também demora mais para esse objeto receber um valor null, então vai demorar mais para o GC coletá-lo.
Acho que o fundamental é você saber em que momentos usar um objeto estático porque o método ser ou não estático não influencia no momento em que o objeto vai ser coletado. Você realmente precisa que o estado do seu objeto/campo seja compartilhado por várias instâncias? Se não precisar, não há motivo para fazê-lo estático.

Agora, quanto ao consumo de memória, sugiro que você use a primeira opção, até porque facilita a compreensão do código e futuras alterações (vai que um dia você precisa dessa mesma instância de FRMConsulta para fazer outra coisa…).

ViniGodoy

A melhor opção mesmo é não usar métodos e variáveis estáticas.

Só use método estáticos diretamente se for para coisas muito simples, tipicamente, que só trabalhem com os parâmetros e não guardem qualquer tipo de estado. É o caso do métodos da classe, Math, por exemplo.

Caso contrário, use o padrão Singleton. Isso faz com que você retorne sempre o mesmo objeto de uma classe não estática, e chame métodos não estáticos. O que aumenta muito a experança de que, no futuro, a classe deixará de ser singleton e você eliminará de vez o uso do static.

No seu exemplo, tanto a opção A quanto B usam singleton, e a diferença entre as duas é praticamente estética… Eu ainda usaria a opção B, que evita a criação de uma variável desnecessariamente, e torna explicito que você está usando um singleton.

J-Chist

Aproveitando o tópico: o método ser estático influencia na retirada do objeto (não-estático) criado por ele da memória?
E criar um objeto fazendo sem atribui-lo a uma referência, tipo

(new Objeto()).fazAlgumaCoisa

consome mais memória? Por que?

J-Chist

Só ter um método factory garante que se está usando Singleton (ou seja, retorna sempre a mesma instância da classe)??? :shock:

E outra coisa, josimarsis:

Em consultar = new FrmConsulta.getInstance(); você não precisa desse new.

ViniGodoy

A principio, uma declaração assim não deveria consumir mais memória:

(new Objeto()).fazAlgumaCoisa();

Se consumir, vai ser algo tão irrisório que não vale a pena você perder noites de sono com isso.

Agora, variáveis estáticas são grandes vilãs em consumo de memória. Não pelo tamanho da variável em si, mas porque, como estáticos tem escopo global, eles nunca são coletados da memória, a menos que sua referência seja explicitamente setada para null.

Veja o caso do padrão singleton, onde você faz:

public class Singleton { private Singleton instancia = new Singleton(); //<--- nunca será coletado public Singleton getInstance() { return instancia; } }

Embora você tenha realmente só uma instância do Singleton na memória, essa instância nunca deixará de existir. É possível fazer um singleton que se auto-coleta usando weak-references e coisas funestas do tipo, o que adiciona uma complexidade incrível ao seu código para um benefício duvidoso.

O problema é quando esse static é um objeto de uma classe complexa, que ocupa muita memória, ou quando a classe permite a adição de listeners. Aí, todos os listeners adicionados ao objeto também não serão coletados, o que pode gerar uma grande bola de neve.

A maior parte dos casos de memory-leaks que já detectei, se dão por statics que fazem coisas desse tipo.

josimarsis

E se eu fizesse assim mudaria em algo?

//Declaração do metodo getInstance() da classe consulta
public static FrmConsulta frmConsulta = null;

    public static FrmConsulta getInstance() {
        if (frmConsulta == null) {
            frmConsulta = new FrmConsulta();
        }
        return frmConsulta;
    }
private FrmConsulta consultar = null;  
 if(consultar == null){  
    //getInstance() metodo estatico  
    //note que agora eu tirei o new
    consultar = FrmConsulta.getInstance();  
 }  
 consultar.consulta(String parametro);

So pra constar eu uso essa tela de consulta em varias telas do sistema e minha intenção em acessa-la estaticamente é evitar de ficar criando instancias desta tela. Fazendo alguns testes tive a impressão que quando utilizo a seguinte maneira:

//Classe A

FrmConsulta.getInstance().consulta(String parametro);

O sistema leva mais tempo para executar a ação, minha impressão procede? Porque?

ViniGodoy

Só ter um método factory garante que se está usando Singleton (ou seja, retorna sempre a mesma instância da classe)??? :shock:

E outra coisa, josimarsis:

Em consultar = new FrmConsulta.getInstance(); você não precisa desse new.

Não garante. Deduzi isso porque o método chama-se getInstance() e não createInstance(). Ou seja, pelo nome, ele estaria retonando sempre a mesma instância.
Claro, ele ainda poderia ter um construtor publico lá, ou outra fábrica, mas seria uma decisão de design realmente estranha ter o construtor e o método estático ao mesmo tempo. Nesse caso, o método estático deveria chamar “getDefaultInstance()”, o que deixa mais explícito que aquela é uma instância específica, e não a única.

ViniGodoy

Não muda em nada. No segundo jeito você cria uma variável temporária, o que leva um tempo irrisóriamente pequeno, e uma quantidade de memória ínfima a mais por alguns milisegundos. No primeiro, você usa o estático diretamente.

Em ambos os casos, tanto a instância, quanto a chamada do método é a mesma. E o consumo de tempo da sua aplicação está no interior dessa chamada.

ViniGodoy

Se quiser tirar a neura, use o profiler do netbeans para medir o tempo das duas chamadas e compare o resultado. Muito provavelmente, você não vai ver qualquer diferença.

josimarsis

Entendi, ViniGodoy levantei esse topico exatamente pq tive um problema de estouro de memoria e entendi o porque ele ocorreu:

O problema é quando esse static é um objeto de uma classe complexa, que ocupa muita memória, ou quando a classe permite a adição de listeners. Aí, todos os listeners adicionados ao objeto também não serão coletados, o que pode gerar uma grande bola de neve.

A maior parte dos casos de memory-leaks que já detectei, se dão por statics que fazem coisas desse tipo.


Realmente carrego alguns Lists em algumas telas e pelo que entendi quando acesso o seu metodo estatico estou carregando eles tambem. A solução seria pensar em alguma forma de setar null nestes objetos após seu uso? Ou existe uma pratica melhor para estes casos?

J-Chist

Hum…Valeu ViniGodoy!

ViniGodoy

Primeiro, é confirmar que o problema está realmente aí. Eu faço isso usando o profiler do netbeans, que indiquei ali em cima.
(Detalhe, a programação faço em Eclipse, mas tenho que tirar o chapéu para aquele profiler).

Se o problema for lá, você tem duas alternativas.

a) Se seu static for realmente necessário: desassociar os objetos relacionados a ele. Limpar listeners, listas, etc.
b) Se houver um momento em que o static puder ser limpo, limpar esse static. Isso limpará também tudo que ele referencia, caso ele seja a única referência. Embora essa alternativa pareça mais simples, ela raramente é possível ou viável.

Mas, realmente, bate aquela tristeza de “achei que estava livre dessa preocupação”, que os programadores C e C++ tem ao entrar no Java. Infelizmente, com variáveis estáticas e de escopo mais global, essa preocupação volta a atormentar nossa vida…

josimarsis

Tudo bem, vou entender o profile do netbeans, analisar meu fonte fazer alguns testes. Assim que tiver algo de concreto ou mais duvidas volto a postar! Até então muito obrigado…

josimarsis

Obrigado a vcs…

josimarsis

Galera surgiu mais uma dúvida. Veja só:

//Exemplo 1 //FazQualquerCoisa() é static ClasseA.FazQualquerCoisa()

Pelo que entendi, se eu precisar chamar esse metodo 2 vezes em uma mesma ClasseX estarei criando 2 objetos estaticos implicitamente, objetos estes que ficariam alocadas na memoria por serem static e por consequencia disso não seriam coletadas pelo Garbage Colector certo?

//Exemplo 2 ClasseA clA = new ClasseA(); //FazQualquerCoisa() é static clA.FazQualquerCoisa();

Usando o Exemplo2 ao acessar 2 vezes o metodo estatico da ClasseA, eu estaria criando um unico objeto não estatico e desta forma ele seria removido pelo Garbage Colector?

josimarsis

Já pesquisei mais ainda não consegui responder essa minha ultima duvida. Alguma luz?

Criado 1 de setembro de 2009
Ultima resposta 3 de set. de 2009
Respostas 18
Participantes 3