Ponto V!

Home C/C++ Conceitos Básicos Problemas com memória
Bruno Crivelari Sanches
Problemas com memóriaImprimir
Escrito por Bruno Crivelari Sanches

Neste artigo vamos discutir dois problemas comuns e causam um bocado de dores de cabeça e as vezes fazem a gente perder preciosas horas de sono, vamos então conhecer os memory leaks e dangling pointers.

Memory Leaks

Memory leak ou vazamento de memória é um problema um tanto comum em C++, principalmente em casos onde o programador tende alocar memória sem necessidade (o que tem se tornado bem comum com programadores que vem de linguagens onde existe um garbage collector) ou não gerencia esta corretamente esquecendo os princípios básicos do RAII.

O memory leak ocorre quando um bloco de memória alocado fica sem referências, isto pode ocorrer em programas bem simples, vejamos um exemplo:

int main(int , char **) 
{ 
    int *p = new int; 
    *p = 3;

    p = new int; 
    delete p;

    return 0; 
}

No exemplo acima, primeiramente é alocado espaço para um int e associado com o ponteiro p, logo após a atribuição do valor “3” é feita uma nova alocação e é neste ponto que o vazamento ocorre.

Note que a memória alocada anteriormente não foi liberada e agora o ponteiro p aponta para outro bloco de memória, a única variável que continha o endereço do primeiro bloco alocado não possui mais, sendo assim, o programa não sabe mais o endereço do bloco e não tem mais como liberar essa memória.

No final da execução desse programa o sistema operacional vai se encarregar de liberar toda a memória do programa, sendo assim este pequeno int que foi perdido não é problema, mas imagine o caso de um programa que tenha que funcionar em um regime 24x7, e a cada segundo ele perde uma centena de ints como aquele, nesse ritmo o programa perde 400 bytes por segundo (não contando aqui o overhead da alocação, que inclui as estruturas de controle do runtime do C++ e do SO).

Em uma hora este programa já vai ter perdido pouco mais de 1 mega de memória, em 24 horas já vão embora 32 mega de memória. Não parece grande coisa, ainda mais hoje em dia que até computador das Casas Bahia vem com 2 gigas de memória, mas lembre-se que esta memória poderia estar sendo útil para outros processos, e no caso de um sistema embarcado este problema é bem grave, um playstation 2, por exemplo, possui apenas 32 mega de ram, então em uma hora já acabamos com boa parte da memória dele.

Dangling Pointers

Os ponteiros “quebrados” costumam ser bem desastrosos e ocorrem quando um ponteiro aponta para uma região de memória “invalida”. Na verdade o ponteiro apontar para algo invalido não é problema, o problema ocorre quando é feita uma tentativa de acesso nesse endereço invalido, vamos a um exemplo básico:

#include <string>

void func() 
{ 
    std::string *str = new std::string("abc"); 
    delete str;

    str->assign("bla bla"); 
}

std::string *func2() 
{ 
    std::string str;

    return &str; 
}

int main(int , char **) 
{ 
    func();

    std::string *str = func2(); 
    str->assign("ops");

    return 0;
}

A primeira função (a func) primeiramente aloca uma std::string, libera ela da memória e depois disso tenta acessa-la. Este é o caso mais comum de um dangling pointer, onde um ponteiro armazena um endereço para um bloco de memória que já foi liberado. Nesse exemplo é muito provável que nada demais aconteça pois o uso é feito logo após a desalocação, mas não podemos esquecer que após um delete estamos dizendo ao sistema que não vamos mais usar aquela memória e ele pode fazer o que bem entender com ela, isso significa que esta memória pode inclusive ser devolvida ao sistema operacional, que pode passar ela a outro processo, sendo que tudo isso pode acontecer antes mesmo da chamada de assign, e quando assign for executado ele vai tentar acessar a memória de outro processo e teremos um belo “ding, este processo executou uma …”.

Em programas mais complexos pode ser que ocorram novas alocações de memória antes que o dangling pointer seja usado, nesse caso aquele bloco que foi liberado já pode estar sendo usado por outro objeto e na chamada de assign estaremos acessando uma memória que pode conter qualquer valor, onde o resultado da operação é como diz a especificação "indefinido”.

A função func2 mostra um outro erro bem comum onde retornamos o endereço de uma variável local, nesse caso após a função retornar o destrutor da string vai ser chamado e sem falar que o endereço que ela “reside” já vai estar disponível para outros usos, novamente é bem provável que o sistema operacional se zangue com seu processo e mate ele.

Um detalhe sobre a segunda função é que muitos compiladores geram um warning quando o programador faz algo desse tipo, sendo assim este problema é fácil de ser evitado.

Evitando Esses Problemas

O jeito mais simples de evitar estes problemas é utilizar alguma técnica de smart pointer, com isso já é possível evitar os memory leaks (tem que apenas tomar cuidado com as referencias circulares).

Utilizando smart pointers é certo que um dangling pointer nunca vai ocorrer pois o smart pointer vai cuidar para que os ponteiros sejam sempre válidos (claro que pode ocorrer uma invasão de memória ou outro evento grotesco).

Infelizmente os memory leaks ainda podem ocorrer devido as referencias circulares e para tentarmos detectar estes precisamos usar outras técnicas, mas isto fica para um próximo post.


Comentários (0)
Escrever um comentário
Your Contact Details:
Gravatar enabled
Comentário:
[b] [i] [u] [url] [quote] [code] [img]   
:angry::0:confused::cheer:B):evil::silly::dry::lol::kiss::D:pinch::(:shock:
:X:side::):P:unsure::woohoo::huh::whistle:;):S:!::?::idea::arrow:
Security
Por favor coloque o código anti-spam que você lê na imagem.
LAST_UPDATED2  

Busca

Linguagens

Twitter