Libertar memoria em c#

Galbne_PT

Power Member
Boas ppl!

Tenho um pequeno grande problema em C#, estou a desenvolver uma aplicação que gere reports de base de dados, ora, para fazer os reports com o formato pedido uso o crystalreports XI.
O que se passa é k cada vez k mostro um report na aplicação a memoria do aumenta bastante ( uns 2 a 5 MB), mas todas a vez k fecho o form que contem o report a memoria mantem-se.

Faço os Dispose() ao report e ao form mas mantem-se td na mm :(

Uso outros resources para por a aplicação mais "user friendly" e faço os disposes a td...

Tive a pesquisar na net sobre o garbage collection do .net, mas n encontra nd significativo que resolva esse problema.

Alguém faz alguma ideia como posso diminuir o tamanho da memoria? (em testes já xeguei aos 100MB :confused: )

Cumps,
 
Isto VB.Net, mas deves conseguir converter para C#

Public Class MemoryManagement

Private Declare Function SetProcessWorkingSetSize Lib "kernel32.dll" ( _
ByVal process As IntPtr, _
ByVal minimumWorkingSetSize As Integer, _
ByVal maximumWorkingSetSize As Integer) As Integer

Public Shared Sub FlushMemory()
GC.Collect()
GC.WaitForPendingFinalizers()
If (Environment.OSVersion.Platform = PlatformID.Win32NT) Then
SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1)
End If
End Sub

End Class

depois so tens de criar um objecto da class MemoryManagemente e executar o Flush:

Dim mm as new MemoryManagement
mm.FlushMemory()
 
Não tem nada a haver :)
No C# (como no JAVA) a memória é limpa automáticamente pelo Garbage Collector.

Pois, é limpa pelo Garbage Collector, até ele limpar a memória vai sp aumentando :(

Teoricamente( visto em documento a falarem sobre o assunto) qd qualquer objecto deixa de ser referenciado é limpo qd o GC executa um processo .net interno para o efeito. (já tive mais de 2 horas com a aplicação a corre após fazer alguns teste e a memoria igual >()

Será que demora mais de 2 horas, hum.. n deve ser, digo eu.
N sei se estou a fazer alguma mal em codigo, mas o dispose() é a maneiera correcta para libertar os objectos? Fica mais uma pergunta no ar :D

Amh vou tentar a solução do iznougud e dps digo os resultados,

Cumps e obrigado a tds!
 
Isto VB.Net, mas deves conseguir converter para C#

Public Class MemoryManagement

Private Declare Function SetProcessWorkingSetSize Lib "kernel32.dll" ( _
ByVal process As IntPtr, _
ByVal minimumWorkingSetSize As Integer, _
ByVal maximumWorkingSetSize As Integer) As Integer

Public Shared Sub FlushMemory()
GC.Collect()
GC.WaitForPendingFinalizers()
If (Environment.OSVersion.Platform = PlatformID.Win32NT) Then
SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1)
End If
End Sub

End Class

depois so tens de criar um objecto da class MemoryManagemente e executar o Flush:

Dim mm as new MemoryManagement
mm.FlushMemory()

Consegui converter para c#, pouca coisa tive de fazer em .net é quase td igual :P, a baixo segue o codigo em C# :D

public class MemoryManagement
{
[DllImport("kernel32.dll")]
private static extern int SetProcessWorkingSetSize(IntPtr process,int minimumWorkingSetSize,int maximumWorkingSetSize);

public MemoryManagement()
{
}

public void FlushMemory()
{
GC.Collect();
GC.WaitForPendingFinalizers();
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1);
}
}


E resolve o problema, pelo menos liberta memoria :D

Obrigado a tds e em especial ao iznougud pela solução

Cumps,
 
Não precisas de libertar memória nenhuma no C#, para isso existe o garbage collector.

Se há várias razões pelas quais o C# é melhor, uma delas é precisamente isso. Ou seja, deixas de usar um objecto e não tens de fazer free à memória que ele usava. A vantagem disto é que, com o garbage collector o processo torna-se bastante mais rápido.

O que acontecia anteriormente era que as pessoas tinham de andar a fazer free's à memória, agora o garbage collector deixa acumular um certo número de objectos a limpar e depois faz o free todo junto, poupando assim bastante tempo... mas bastante tempo mesmo!!

http://www.samspublishing.com/articles/article.asp?p=101373&seqNum=13&rl=1

Se quizeres forçar a limpeza de memória:

Forcing a Garbage Collection
Here's one last thing to know about C# garbage collection—although the C# documentation (and many C# books) says you can't know when garbage collection will take place, that's not actually true. In fact, you can force garbage collection to occur simply by calling the System.GC.Collect method.

This method is safe to call, but if you do, you should know that execution speed of your code might be somewhat compromised.

Mas é como te digo, é uma perda de tempo fazeres isto.
 
CollZero é td verdade o que dizes, todos os textos que li sobre o assunto dizem para deixar o Garbage Colletor tratar do assunto de libertar a memória, mas como disse anteriormente após alguns testes e ao fim de 2 horas com a aplicação aberta a memoria era a mesma :( isso sem minimizar a aplicação (qd minimizas ele faz o System.GC.Collect), ora uma aplicação com 70/80MB de memoria n posso permitir que tal aconteça e ainda por cima é memoria lixo, porque os objectos estão tds prontos para serem limpos simplesmente n são.

Perco eficiencia, mas pelo menos liberto memoria para outras aplicações correrem à vontade e n "dizerem" que a minha aplicação ocupa mt memoria e n deixa os outros correrem :D

Cumps,
 
CollZero é td verdade o que dizes, todos os textos que li sobre o assunto dizem para deixar o Garbage Colletor tratar do assunto de libertar a memória, mas como disse anteriormente após alguns testes e ao fim de 2 horas com a aplicação aberta a memoria era a mesma :( isso sem minimizar a aplicação (qd minimizas ele faz o System.GC.Collect), ora uma aplicação com 70/80MB de memoria n posso permitir que tal aconteça e ainda por cima é memoria lixo, porque os objectos estão tds prontos para serem limpos simplesmente n são.

Perco eficiencia, mas pelo menos liberto memoria para outras aplicações correrem à vontade e n "dizerem" que a minha aplicação ocupa mt memoria e n deixa os outros correrem :D

Cumps,

Tens de compreender que se o sistema não está a necessitar dessa memória, ela estar ocupada ou não é competamente indiferente. Ou seja, o pior que pode acontecer é um programa qq necessitar de memória e ela será liberta nessa altura.
 
Galbne_PT:
O problema não é com C# é com o Cristal Reports.
Tive o mesmo problema com o Cristal Reports numa aplicação que desenvolvi e experimentei várias versões do Cristal Reports e sempre com o mesmo resultado: por cada vez que é utilizado, "come" memória. Este aumento de memória acontece quando se faz o setdatasource(object) do Cristal Reports.
No meu caso, como tinha que gerar muitos reports sequêncialmente a aplicação acabava por rebentar com "out of memory exception" gerado pelo código do setdatasource(object) do CR.
O problema é que embora as dll's do Cristal Reports que se referência serem .NET e portanto, serem managed code, as dll's em que o Cristal Reports está implementado são numa linguagem unmanaged code #1 (provavelmente C e C++) e é algures dentro destas dll's que os recursos utilizados não são libertados.

#1 As dll's do Cristal Reports referênciadas no visual studio são apenas uns wrappers ("pontes") em .NET para as dll's que realmente contêm o código do Cristal Reports numa linguagem unmanaged code.
 
De qqr modo, a ideia de que um garbage collector é obviamente melhor que um humano, é basicamente errada. A ideia é como a de que os optimizing compilers criam sempre melhor assembler que o melhor coder humano, e sabemos todos que isso é falso.

Os garbage collectors fazem um trabalho semelhante ao dos optimizing compilers. O de reduzir o fosso entre os coders médios e os realmente bons. Lamentavelmente, nem de perto nem de longe lá chegam.

Como tu dizes CoolZero, a mem não está a ser usada. A questão é que provavelmente um humano poderia tê-la liberto durante idle time do cpu quando não estava a ser feito nada, e o GC pode deixar passar a oportunidade e ter de fazê-lo durante 100% Load, quando menos convem. Em vez de aproveitares uma janela de oportunidade para fazer o cleanup, vais fazê-lo quando menos devias, pq o sys precisou da mem e o GC vai ter de "dá-la".

Soluções automágicas são porreiras mas raramente funcionam de modo brilhante. Era melhor ensinar o ppl a fazer as coisas direitas. SCSI Scam com periféricos que se estavam borrifando, Plug'n'Play com devices que continuavam a fazer o que queriam, been there done that. O Autoconfig funcionava bem no Amiga pq tinhas 256 IRQ's para 4/6 placas... E ainda assim tarda não calha tinhas de fazer swap de algumas... E tens pilhas de bons exemplos de boas ideias "automágicas" que não funcionaram, é só procurar.
 
Galbne_PT:
O problema não é com C# é com o Cristal Reports.
Tive o mesmo problema com o Cristal Reports numa aplicação que desenvolvi e experimentei várias versões do Cristal Reports e sempre com o mesmo resultado: por cada vez que é utilizado, "come" memória. Este aumento de memória acontece quando se faz o setdatasource(object) do Cristal Reports.
No meu caso, como tinha que gerar muitos reports sequêncialmente a aplicação acabava por rebentar com "out of memory exception" gerado pelo código do setdatasource(object) do CR.
O problema é que embora as dll's do Cristal Reports que se referência serem .NET e portanto, serem managed code, as dll's em que o Cristal Reports está implementado são numa linguagem unmanaged code #1 (provavelmente C e C++) e é algures dentro destas dll's que os recursos utilizados não são libertados.

#1 As dll's do Cristal Reports referênciadas no visual studio são apenas uns wrappers ("pontes") em .NET para as dll's que realmente contêm o código do Cristal Reports numa linguagem unmanaged code.

Já suspeitava que o problema fosse mm do crystal, mas como são .net(esta no manual, n é que o tenha lido de cima a baixo :P).
Mt mal que a microsoft seja :002: :lol:
n podia dizer que um produto na teoria faça uma coisa e dps na pratica n faz.

De qqr modo, a ideia de que um garbage collector é obviamente melhor que um humano, é basicamente errada.

Concordo :P

Cumps
 
De qqr modo, a ideia de que um garbage collector é obviamente melhor que um humano, é basicamente errada. A ideia é como a de que os optimizing compilers criam sempre melhor assembler que o melhor coder humano, e sabemos todos que isso é falso.

Os garbage collectors fazem um trabalho semelhante ao dos optimizing compilers. O de reduzir o fosso entre os coders médios e os realmente bons. Lamentavelmente, nem de perto nem de longe lá chegam.

Como tu dizes CoolZero, a mem não está a ser usada. A questão é que provavelmente um humano poderia tê-la liberto durante idle time do cpu quando não estava a ser feito nada, e o GC pode deixar passar a oportunidade e ter de fazê-lo durante 100% Load, quando menos convem. Em vez de aproveitares uma janela de oportunidade para fazer o cleanup, vais fazê-lo quando menos devias, pq o sys precisou da mem e o GC vai ter de "dá-la".

Soluções automágicas são porreiras mas raramente funcionam de modo brilhante. Era melhor ensinar o ppl a fazer as coisas direitas. SCSI Scam com periféricos que se estavam borrifando, Plug'n'Play com devices que continuavam a fazer o que queriam, been there done that. O Autoconfig funcionava bem no Amiga pq tinhas 256 IRQ's para 4/6 placas... E ainda assim tarda não calha tinhas de fazer swap de algumas... E tens pilhas de bons exemplos de boas ideias "automágicas" que não funcionaram, é só procurar.


Básicamente o problema é q 99.9% dos programadores não sabe quando é essa janela de oportunidade que tu falas, logo vão fazer free à memória em alturas pouco convenientes. Isto já foi abordado em vários "encontros"/congressos acerca de framework .net. Normalmente quando alocas a memória vais liberta-la no final da função ou mal saibas q não a vais usar mais, n tens um array de ponteiros/objectos (tipo trash can) ao qual recorres para libertar memória assim que sabes que é a altura ideal.

Um exemplo muito simples para verificares como é ineficiente fazeres tu os frees, se bem q é um exemplo um pouco a puxar a brasa para a framework, é teres um código simples como:

Código:
int main()
{
    char* str;   
    while(true)
    {
       str = malloc(sizeof(char)*256);
       printf("%s", str);
       free(str);
    }
}

agora em C#

while(true)
{
char[] str = new char[256];
Console.Write(str);
}

Experimenta correr este código em máquinas iguais e vê a diferença de velocidade entre um e outro. Básicamente o free está a consumir muito tempo.. enquando na framework n é libertada a memória logo, por isso vês as coisas serem feitas muito mais rápidamente.
 
Puxar a brasa é pouco. O primeiro exemplo força um free a cada ciclo, o segundo liberta quando o GC se lembrar. Experimenta meter um force no 2º e logo vês como o GC se comporta bem... E qual é a diferença real entre uma coisa e outra. Uma coisa é tu libertares a ram que sabes que não precisas, outra é um GC a "varrer os cantos á casa" á procura dela.
 
Também n é assim, o GC n anda à procura pelos cantos da casa do q libertar. À partida existe uma série de objectos não referenciados em memória que ele sabe que pode libertar.
Quanto ao exemplo era mesmo isso que queria dizer... em C tu forças os frees e, embora não seja dentro de um ciclo assim, é em alturas que não são as mais convenientes, pois se fosse mais tarde terias de andar a guardar as referencias paras os objectos que querias libertar.
 
ShadeX e CoolZero vejo que cada um bem a sua opiniao e a qual são opostas, ao qual respeito.

É mt positivo estarem a mostrar as suas opinioes e divergencia relativo ao tema.

Mas, como sei pouco de GC, gostava de deixar mais uma pergunta. Relativo a meu caso pratico,
eu optei por forçar o GC limpar a memoria depois de mostrar os reports, pois são eles que ocupam mais memoria.
A pergunta é....
Não é melhor forçar o GC, mm sendo ineficiente, mas para o utilizador do windows (mais propriamente o meu patrao) ver a aplicação com 10/15MB do que 70/80MB?
Perde-se perfomance, mas o utilizador n nota mt.

É a portuga, coisas bonitas, mm n sendo boas, são as k têm mais sucesso :P

Obrigado desde já por tds as respostas.

Cumps.
 
pois se fosse mais tarde terias de andar a guardar as referencias paras os objectos que querias libertar.

Ou seja, tinhas de andar a fazer o trabalho do GC, com a diferença que podias tomar decisões mais "á medida" ,)

Sabes, nos dias de hoje, já nem sei a razão de ser de discussões destas. Tens ram ao pacote, tens hd ao pacote, tens um OS que facilita tudo e mais alguma coisa...

Provavelmente não tiveste o "previlégio" de trabalhar com um OS tipo AmigaOS. Não havia protecção de memória, não havia memória virtual, não havia muito de nada... Alturas em que 4MB era um luxo do cacete e um bad pointer limpava o OS e tudo o resto. Curiosamente, tinhas montes de apps a funcarem bem, tão rápido quanto podiam, a dar-se bem umas com as outras, a partilhar o pouco que tinham. Tinhas developers a sério tbm. Atm tens OSs/APIs que te protegem de tudo, que limitam o que podes fazer para não asneirares, que te forçam a fazer o que não queres "por questões de segurança", etc etc etc... E tens mais patches post-release que nunca, pq niguem testou a sério a coisa, e quando sai do dev env e vem para o mundo real, a bronca é do cacete...

Galbne_PT:

Eu sinceramente não me importo com a RAM, a menos que se torne um problema. E se vais forçar o GC, tenta fazer isso com pés e cabeça. Tens counters de cpu usage, checka primeiro os bixos. Nada mais maravilhoso que tu tentares libertar RAM enquanto uma app está a tentar fazer alguma coisa.
 
Não cheguei a trabalhar com AmigaOS, só mesmo com Unix/Linux em que muitas das optimizações que fazia não eram propriamente ao nível do espaço mas sim ao nível da performance de execução. Acredito que nesses tempos fosse bastante importante o contrário, demorar o tempo que for necessário, mas ao menos ser feita qq coisa :) Apanhei com mts segmentation faults ;) Ou seja, a protecção de memória a funcionar :)

Só trabalhei com endereçamento real quanto programei em Assembly para 8086, mesmo assim corria num simulador, não apagava nada que não fosse importante a não ser coisas minhas.

Mas hoje em dia, com os sistemas operativos existentes, não há necessidade desse tipo de coisas, até porque a protecção de memória veio responder a 99% dos ataques que eram executados nos computadores, para além das asneiras. Mas lá está, antigamente quando necessitavas de gerir a memória, muito dificilmente o fazias de forma eficiente, hoje, como não tens essa necessidade, fazes as coisas de forma "mais" eficiente.

Galbne_PT, tal como o Shadex disse, podes verificar primeiro o nível de cpu usado... ou então aproveitar o terminar de alguns eventos em que sabes que o utilizador acha "normal" existir um tempo de espera... Por exemplo, quando carregas num botão e a aplciação fica um ou dois segundos sem fazer nada, é desagradável, pensas q n carregaste no botão e vais lá carregar várias vezes :P portanto essas não são boas alturas, contudo, quando fechas uma janela, por exemplo a do crystal report, pode muito bem demorar um segundo a fechar... (convém pores o cursor sempre em ampulheta)
 
Não cheguei a trabalhar com AmigaOS, só mesmo com Unix/Linux

Pensa num *nix com menos OS. E de acordo com as más linguas, um cadito mais organizado. Aliás, a grande cena do AmigaOS, mesmo defunto, é que na altura pegou nos fundamentos de *nix, massajou e compactou os ditos e fez-se qqr coisa de impressionante. O teu Win actual é uma tentativa mais ou menos bem sucedida de passar de uma legacy de m***a dos tempos do MSDOS para uma coisa mais sólida, ...tipo... *nix :D

Se quiseres ver como era, experimenta o WinUAE. Se quiseres ter uma ideia de como era duro, corres em emul fiel de A500. E ficas a pensar como raio se fez aquilo... 512K de OS em ROM, 512K de RAM, uma disquete de 800K, um cpu de 7Mhz, presto, preemptive multitasking com GUI e tudo... E o desktop azul e laranja nem era muito mau nos 80's :P Mas pronto, dos fracos não reza a história, e a máquina (marca) não aproveitou a janela de oportunidade que teve. Fica para a história no entanto.
 
GC

Galbne_PT:
O problema não é com C# é com o Cristal Reports.
Tive o mesmo problema com o Cristal Reports numa aplicação que desenvolvi e experimentei várias versões do Cristal Reports e sempre com o mesmo resultado: por cada vez que é utilizado, "come" memória. Este aumento de memória acontece quando se faz o setdatasource(object) do Cristal Reports.
No meu caso, como tinha que gerar muitos reports sequêncialmente a aplicação acabava por rebentar com "out of memory exception" gerado pelo código do setdatasource(object) do CR.
O problema é que embora as dll's do Cristal Reports que se referência serem .NET e portanto, serem managed code, as dll's em que o Cristal Reports está implementado são numa linguagem unmanaged code #1 (provavelmente C e C++) e é algures dentro destas dll's que os recursos utilizados não são libertados.

#1 As dll's do Cristal Reports referênciadas no visual studio são apenas uns wrappers ("pontes") em .NET para as dll's que realmente contêm o código do Cristal Reports numa linguagem unmanaged code.

True.

Neste caso especifico deves usar o Garbage Collector.
Repito: normalmente não deves usar o GC, mas este Interop (Wrapper para com) necessita de ser libertado excepcionalmente.

no fim das tuas funções, coloca os objectos previamente instanciados a null. (good practice)
no unload da tua form/web form. coloca GC.Collect();

/ing
 
Back
Topo