Metodo System.gc();

Vai me desculpar, mas não existe qualquer lógica em seus argumentos. Não há qualquer analogia entre portabilidade e a chamada ao gc. Não estavamos falando em gerenciar o gc na mão, mas em informa-lo que uma sessão crítica do nosso software vai começar e que, no passado, pode haver muita coisa criada e que pode ser coletada. Trata de dizer ao gc que se ele puder limpar agora para não limpar depois, melhor.

E qual é o conceito de se ter um gc?

[quote=sergiotaborda]O GC não é uma api bacana de gerenciamento de memoria que vc usa nos seu programas C que vc precisa ficar tunando.
O GC é algo à parte. Tão isolado quanto o compilador JIT. Tentar comandar estas coisas em java é como , em C, tentar comandar o que o processador irá fazer a seguir.

A seguir vc vão querer ter controle determinista sobre threads…
Não ha porquê e a sun/jsp faz muito bem em desacoplar as coisas.[/quote]

Engraçado, o próprio gc da sun tem diversas opções de tuning, inclusive com documentação sobre como fazer isso. E o método System.gc() existe. A próxima versão de gc poderá até ter metas de desempenho, fornecidas pelo programador. A argumentação da Sun contra o gc nunca envolveu “o conceito de gc” e sim, dificuldades técnicas de implementação ou liberdade de implementação futura.

Vamos lá a ver uma coisa, devo tar falando spranglish…

incovar System.gc() não dá garantia de chamada do CG. Pensemos que temos duas implementações de GC , A e B.
A acata a chamada a gc () como a da sun. B, não. simplesmente ignora.

Agora vc desenvolve um sistema usando a jvm com o cg A. Vc descobre que tem problemas de memoria /performance. Vc então decide chamar system.gc() e descobre que isso resolve o seu problema. Pronto, a sua aplicação está pronta.

Vc manda a aplicação para o cliente que usa uma jvm que corre um cg do tipo B. A sua aplicação continua apresentando o mesmo problema de performance que antes pois a chamada a .gc() não faz nada. A portabilidade da sua aplicação foi pró espaço.
Das duas uma : ou vc estabelece “esta aplicação só funciona na jvm com cg do tipo A” e deixa o cliente se virar para arranjar uma, ou
vc altera a sua aplicação para não precisar invocar o .gc().

É óbvio que .gc() estabelece uma dependencia da JVM. E , como se lembrarão, a jvm da sun não é a única do mundo.

Este cenário é ainda pior em ambiente embarcado. Aqui a chance de usar uma jvm sun é pequena e a chance de estar usando uma jvm tipo B é grande. No cenário embarcado é ainda mais importante a portabilidade e amarrar a performace ao tipo de GC usado por baixo dos panos não é bom. Claro que aqui tb vale a opção “aplicação homologada para o aparelho,X,Y,Z,…”

Existe um outro mundo, e tlv seja esse em que o vini e o julio vivem, em que uma aplicação é feita e otimizada para um certo ambiente. Isso é real e funciona. Até que o ambiente muda e começa tudo de novo. Isso não é mais profiling da aplicação, mas sim do stack inteiro. Isso é tudo muito bom, mas não é o espirito de uma app java. Se vc vive nesse mundo tlv outras plataformas sejam melhores. Java ainda não é para coisa realtime.

O ponto é o seguinte : vc pode informar o gc que agora seria uma boa hora para corre-lo ? sim. isso se chama educação.
A performance da sua aplicação pode depender de chamar ou não o gc ? Não. Isso é uma amarração perigosa. É como usar uma API que só funciona no windows. Deve ser feito ? Não. Pode ser feito ? Pode, mas a custo da aplicação não ser mais portável.
Se a aplicação não é mais portável não ha porque fazê-la em java. Use alguma plataforma nativa ao OS (.NET no windows, C++ no linux , Objetive-C no mac, etc…)

Ok, mesmo sabendo isso ainda prefere java devido a outras facilidades ? Ok. Mas não espere que a especificação da JVM mude para acomodar esse tipo de aplicação que vai contra o costume e os bons princípios da plataforma.

[size=9]
E só um adendo, não de pode dizer que a JVM delega o tratamento de threads para o OS. Não está especificado o que ela faz em relação a isso.
No windows e no linux e na jvm da sun até pode ser que seja assim, mas não podemos partir disse principio ( o que aliás não adiantaria de nada).
Em alguns OS (como no Palm) onde não ha thredas nativas nem processos, a jvm tem que usar green threads. A área de threads é uma das que menos garantias tem. Básicamente vc sabe que run() será chamado. Não sabe como,quando nem por quem.[/size]

Ok, sob esse ponto, você está certo. Ele fica vinculado a uma única VM. A sorte é que se vincular a VM da Sun, vc estará se vinculando a aproximadamente 90% das VMs do mecado, pelo menos no caso das aplicações web e desktop. Outros ambientes já não são muito multiplataforma mesmo, e sua aplicação acabará um tanto específica.

Agora, note que hoje o comando só fere uma boa prática de programação justamente pelo seu caráter opcional, que é exatamente o que estamos argumentando contra. Tanto eu quanto o julio sabíamos que esse comando não seria uma panacéia. É na verdade um placebo. Veja que, mesmo que ele seja obrigatório, uma fase de um jogo longa, ou um vídeo longo ainda poderão gerar coletas de lixo… E, realmente, é uma coisa que teremos que tolerar.

Por isso outros defensores de mais controle no gc() sugerem uma forma de paralizar o gc completamente e reativa-lo mais tarde. Essa seria a única forma de garantir algum comportamento de tempo real por algum tempo. Seria isso ir contra o princípio da coleta de lixo, ou do Java? Há muita gente que acha que não e eu me encontro entre elas.

Por outro lado, o java permaneceria inadequado para aplicações de tempo real, pois não é possível manter o coletor de lixo desligado para sempre. Por isso, embora eu ache que esse conceito não seja “filosoficamente antagônico”, não vejo a introdução de um comando desses como algo tão útil assim.

A solução encontrada pela sun foi intermediária e, para o caso do Java, eu considero adequada. Ela vai permitir que o gc assuma compromissos de tempo, dentro da capacidade do hardware e das configurações que estiver rodando. Ele fará isso coletando menos lixo, ajustando melhor o momento das coletas longas, entre outras coisas. Por mim, isso já é mais do que suficiente, e uma solução muito mais elegante do que fazer o programador controlar diretamente o gc. Nada mais é do que melhorar os parâmetros de tuning.

[quote=ViniGodoy]
Por isso outros defensores de mais controle no gc() sugerem uma forma de paralizar o gc completamente e reativa-lo mais tarde. Essa seria a única forma de garantir algum comportamento de tempo real por algum tempo. Seria isso ir contra o princípio da coleta de lixo, ou do Java? Há muita gente que acha que não e eu me encontro entre elas.

Por outro lado, o java permaneceria inadequado para aplicações de tempo real, pois não é possível manter o coletor de lixo desligado para sempre. Por isso, embora eu ache que esse conceito não seja “filosoficamente antagônico”, não vejo a introdução de um comando desses como algo tão útil assim.

A solução encontrada pela sun foi intermediária e, para o caso do Java, eu considero adequada. Ela vai permitir que o gc assuma compromissos de tempo, dentro da capacidade do hardware e das configurações que estiver rodando. Ele fará isso coletando menos lixo, ajustando melhor o momento das coletas longas, entre outras coisas. Por mim, isso já é mais do que suficiente, e uma solução muito mais elegante do que fazer o programador controlar diretamente o gc. Nada mais é do que melhorar os parâmetros de tuning. [/quote]

Mas o que importa dessa hsitoria é que isso foi possivel porque é possivel modificar o CG sem ter que modificar as aplicações.

O meu ponto é que o CG é um componente automático, no sentido de “não põe aqui a mão , mané!” (hands-off)
Se for colocada algum método que permite controlar o que o CG faz ou como ou quando faz, ele deixa de ser automático.
Coisas informativas (hints) tudo bem, porque em ultimo caso sempre será o CG a decidir. Isto seria um tuning, mas um tuning não dependente. Neste tipo de API não vejo nenhum mal tecnico. O problema é conceptual como no caso de threads.

A JVM da Sun acata o .gc() se vc chamar com parcimónia, mas será que acata se chamar a cada método ? Convenhamos que se sim, essa não é uma boa implementação do GC. Este tipo de recurso tem que ser protegido do abuso do programador.

O “vedadeiro” real time não se faz com um GC melhor, se faz com uma JVM diferente. A melhor forma de garantir que o GC não afetará o tempo da aplicação é não ter nenhum lixo para coletar. Ora isso só é possivel se não houver nenhum objeto “morto” no heap, e isso só é possivel se o objeto estiver no stack…pois, só que objetos não ficam no stack…ficam ?
Na real time JVM ficam. Nem todos os objetos são aptos a ficam no stack, mas a JVM faz um exercicio interessante ao não popular o heap indescriminadamente. Esta lógica é critica em real time verdadeiro (do qual vidas dependem) mas com o tempo poderá ser o padrão da jvm em que heap e stack passará a ser uma diferenciação semântica. Tudo isto só para dizer que a resposta java para o real time não é (apenas) mudar o GC.

A resposta para realtime é não usar máquina virtual. Deve-se fugir de código intermediário em tempo de execução.
Sobre o gc, seria muito interessante essa opção. Cada situação envolve um tipo de ação diferente, e, infelizmente, o coletor de lixo não consegue prever isso.
Poder ter opção, sempre é bom.

[quote=juliocbq]A resposta para realtime é não usar máquina virtual. Deve-se fugir de código intermediário em tempo de execução.
Sobre o gc, seria muito interessante essa opção. Cada situação envolve um tipo de ação diferente, e, infelizmente, o coletor de lixo não consegue prever isso.
Poder ter opção, sempre é bom.[/quote]

Hoje em dia eu não usaria java para realtime, mas o futuro do realtime é sim dentro de uma vm.
A razão é obvia : o controle de tempo não depende do tipo de aplicação . Se com VM conseguimos ter
aplicações mais rápidas que com codigo estáticamente compilado não seria surpreendente que a VM
possa dar garantia de quanto uma execução demoraria. Fora que a analise dinâmica pode encontrar
padrões que podem ser tratados especialmente . Não é coisa para agora, mas no futuro breve ( mais 5 ou 10 anos).

Eu espero que sim. Mas sobre tempo e performance de aplicações de máquina virtual serem mais rápidas que código nativo, é pura ilusão. A maioria dos benchmarks que afirmam que um bytecode roda mais rápido que código nativo, estão, na maioria(80%) furados.

Fiz uma pesquisa com benchmarks uma vez, e descobri que a velocidade do bytecode sobre o nativo, se deve a otimização que a máquina virtual faz. Então peguei o mesmo software, este em c++, e aumentei a otimização, nos parâmetros do compilador, para nivel 2(nem setei compilação para processador específico), e tive resultados completamente diferentes. Fiz com vários benchmarks que encontrei no google, que diziam que a vm pode rodar mais rápido que código nativo. E todos os testes falaram o contrário.

Além disso, alguns benchmarks tendenciosos comparavam código compilado sem otimização com o Visual Studio 6.0 (compilador bem antiguinho e bem ingênuo, lançado em 1998 ) com o Java 6.0). Se comparar com o mesmo compilador, só que na versão 2008 e código otimizado, o código em C++ costuma ganhar.

Um ponto positivo para o Java é o fato de que a VM e os dados de reflection permitem instrumentação do código não intrusiva e de extrema qualidade. Como a obtida no visual VM, por exemplo.

Conseguimos obter diversas aplicações Java mais rápidas do que as feitas em C++, não pelo fato do Java ser uma linguagem nativamente mais rápida, mas pelo fato de ser facílimo instrumentar, descobrir gargalos, corrigir erros e obter algoritmos mais eficientes.

No C++, dificilmente pode-se fazer instrumentação sem inserir instruções de profiling dentro do código.

[quote=ViniGodoy]Um ponto positivo para o Java é o fato de que a VM e os dados de reflection permitem instrumentação do código não intrusiva e de extrema qualidade. Como a obtida no visual VM, por exemplo.

Conseguimos obter diversas aplicações Java mais rápidas do que as feitas em C++, não pelo fato do Java ser uma linguagem nativamente mais rápida, mas pelo fato de ser facílimo instrumentar, descobrir gargalos, corrigir erros e obter algoritmos mais eficientes.

No C++, dificilmente pode-se fazer instrumentação sem inserir instruções de profiling dentro do código.[/quote]

Sim, é verdade, mas os testes são feitos usando programas simples. Normalmente um laço for sem nada dentro, e outras instruções contando tempo e processando calculos matemáticos.

Esse for vazio é descartado pelo otimizador da jvm, enquanto em um compilador, é necessário setar confugurações. Fazendo isso descobri uma série de Benchs inconsistentes, no google.

Normalmente, o resultado é c++ ser 1 tempo, mais rápido que java. E vendo por esse lado, java é a vm mais rápida delas.

Outra coisa que observamos é o fato do new de delete do Java ser extremamente rápido.

Faça um teste com a criação e destruição de um objeto dentro de um loop. O C++ tem uma implementação bastante inocente: a cada new, reserva-se memória, a cada delete, apaga-se. Isso é feito por solicitações ao SO, extremamente lentas.

No caso do Java, a VM controla os objetos de vida curta. Quando um objeto é deletado, sua memória não é imediatamente desalocada e só ponteiros da VM são atualizados. Ou seja, equivale a uma deleção de custo próximo ao 0. Se um objeto idêntico é requisitado, essa memória é devolvida, quase instantaneamente.

Isso parte de uma observação de que objetos de vida curta estão muito presentes. Segundo os artigos da sun, eles correspondem a mais de 80% dos casos, em média. Tem mais detalhes desse funcionamento aqui: http://www.ibm.com/developerworks/java/library/j-jtp11253/

Para testar, basta fazer algo como:

for (int i = 0; i < 10000000; i++) { Teste t = new Teste(); delete t; }

Tente usar um objeto com vários campos, para o resultado ser mais evidente, e procure zera-los no construtor, como o java e uma aplicação normal fariam.

No C++, é possível fazer otimizações nesse sentido se você sobrecarregar o new e o delete, e fazer você mesmo um memory manager. E, obviamente, essa é uma tarefa extremamente complexa. Claro, no caso do loop acima, seria possível criar t antes do laço e só zerar os valores de t, ao invés de usar new e delete sempre. Mas isso não seria possível caso t fosse um objeto temporário criado numa função, que estivesse sendo chamada por esse laço.

A implementação do Java, entretanto, come mais memória. É o velho trade-off de velocidade versus memória.

[quote=ViniGodoy]Outra coisa que observamos é o fato do new de delete do Java ser extremamente rápido.

Faça um teste com a criação e destruição de um objeto dentro de um loop. O C++ tem uma implementação bastante inocente: a cada new, reserva-se memória, a cada delete, apaga-se. Isso é feito por solicitações ao SO, extremamente lentas.

No caso do Java, a VM controla os objetos de vida curta. Quando um objeto é deletado, sua memória não é imediatamente desalocada e só ponteiros da VM são atualizados. Ou seja, equivale a uma deleção de custo próximo ao 0. Se um objeto idêntico é requisitado, essa memória é devolvida, quase instantaneamente.

Isso parte de uma observação de que objetos de vida curta estão muito presentes. Segundo os artigos da sun, eles correspondem a mais de 80% dos casos, em média. Tem mais detalhes desse funcionamento aqui: http://www.ibm.com/developerworks/java/library/j-jtp11253/

Para testar, basta fazer algo como:

for (int i = 0; i < 10000000; i++) { Teste t = new Teste(); delete t; }

Tente usar um objeto com vários campos, para o resultado ser mais evidente, e procure zera-los no construtor, como o java e uma aplicação normal fariam.

No C++, é possível fazer otimizações nesse sentido se você sobrecarregar o new e o delete, e fazer você mesmo um memory manager. E, obviamente, essa é uma tarefa extremamente complexa. Claro, no caso do loop acima, seria possível criar t antes do laço e só zerar os valores de t, ao invés de usar new e delete sempre. Mas isso não seria possível caso t fosse um objeto temporário criado numa função, que estivesse sendo chamada por esse laço.

A implementação do Java, entretanto, come mais memória. É o velho trade-off de velocidade versus memória.[/quote]

Não seria possível?

for (int i = 0; i < 10000000; i++) {
  Teste t;
  ZeroMemory(&t,sizeof(t));
}

Imagino aqui o tempo ser bem mais curto. Sendo ZeroMemory escrito diretamente em assembly.

Seria, mas considere o caso:

for (int i = 0; i < 10000000; i++) { t.doSomething(); }

E o método doSomething() faz a tal criação e destruição temporárias. No benchmark o problema fica explicito. na vida real, nem sempre.
E considere também que a classe do objeto t não foi implementada por você, mas vem de uma API externa… e aí você já viu o problema.

[quote=ViniGodoy]Seria, mas considere o caso:

for (int i = 0; i < 10000000; i++) { t.doSomething(); }

E o método doSomething() faz a tal criação e destruição temporárias. No benchmark o problema fica explicito. na vida real, nem sempre.
E considere também que a classe do objeto t não foi implementada por você, mas vem de uma API externa… e aí você já viu o problema.[/quote]

Ae sim. Isso é um problema, justamente por eu não ter controle.

Eu adoro quando o desenvolvedor utiliza System.gc() dentro do servidor de aplicação. Principalmente quando coloco no parâmetro de inicialização da JVM: -XX:DisableExplicitGC :twisted:

Isso só vai fazer com que o comando seja desobedecido sempre. Mas não vai gerar nenhum tipo de erro.
Se o desenvolvedor depende vitalmente dessa chamada, está realmente cometendo um erro fatal.

E detalhe, para esse comando ter efeito, você precisa usar o sinal de +, não o de -. É o problema com opções na negativa. Se você usar com -, estará habilitando o explicit gc (como já é padrão), já que estará desligando o “disable”.

[quote=ViniGodoy]Isso só vai fazer com que o comando seja desobedecido sempre. Mas não vai gerar nenhum tipo de erro.
Se o desenvolvedor depende vitalmente dessa chamada, está realmente cometendo um erro fatal.[/quote]

ainda bem que ele não é meu gerente. Se bem que eu nem trabalho com servidores. A maioria dos softwares são simples, e só controlam hardware.

[quote=ViniGodoy]Isso só vai fazer com que o comando seja desobedecido sempre. Mas não vai gerar nenhum tipo de erro.
Se o desenvolvedor depende vitalmente dessa chamada, está realmente cometendo um erro fatal.[/quote]

Essa é a idéia. Conforme apontado aqui neste tópico, através de System.gc() ele sugere que passe o coletor… simplesmente dou uma forcinha para que a sugestão nunca seja aceita. Confio mais na gestão da coleta pela JVM do que pelo desenvolvedor (em ambientes servidor).

Não é bem assim Vini. Na verdade -XX faz parte do prefixo da diretiva, o que esqueci de digitar foi o + antes do DisableExplicitGC:

-XX:+DisableExplicitGC

[quote=Alessandro Lazarotti][quote=ViniGodoy]Isso só vai fazer com que o comando seja desobedecido sempre. Mas não vai gerar nenhum tipo de erro.
Se o desenvolvedor depende vitalmente dessa chamada, está realmente cometendo um erro fatal.[/quote]

Essa é a idéia. Conforme apontado aqui neste tópico, através de System.gc() ele sugere que passe o coletor… simplesmente dou uma forcinha para que a sugestão nunca seja aceita. Confio mais na gestão da coleta pela JVM do que pelo desenvolvedor (em ambientes servidor).

Não é bem assim Vini. Na verdade -XX faz parte do prefixo da diretiva, o que esqueci de digitar foi o + antes do DisableExplicitGC:

-XX:+DisableExplicitGC

existem vantagens e desvantagens.
Como citado, o gc é “burro”. Em muitos casos, você precisa ajustar para sua situação.


http://www.oracle.com/technology/global/lad-pt/pub/articles/brahms-tuning.html