Desenvolvimento - Mobile

HB++: Gerenciamento de Memória no Palm OS

O sistema operacional Palm OS possui basicamente dois tipos de memória utilizados pelos aplicativos: Memória de Alocação Dinâmica e Memória de Armazenamento.

por Caio Proiete



HB++: Gerenciamento de Memória no Palm OS

Introdução

O sistema operacional Palm OS possui basicamente dois tipos de memória utilizados pelos aplicativos: Memória de Alocação Dinâmica e Memória de Armazenamento. A Memória de Alocação Dinâmica, conhecida por HEAP, é a memória utilizada para armazenar todas as variáveis criadas pelas aplicações, no momento da execução, enquanto a Memória de Armazenamento, conhecida por STORAGE, é a memória onde estão gravados todos os programas e bancos de dados do Palm OS.

O tamanho dessas duas memórias não é padrão, e varia em cada PDA/SmartPhone, por exemplo, o Palm T|X possui aproximadamente 6 Mb de memória de alocação dinâmica (heap) e 64 Mb de memória de armazenamento (storage), enquanto o Treo 650 possui aproximadamente 5 Mb de memória de alocação dinâmica (heap) e 22 Mb de memória de armazenamento (storage).

Conceito

O Handheld Basic possui uma função interna chamada MemInfo, que nos permite obter informações sobre as duas memórias (heap e storage), de acordo com o parâmetro informado, que pode ser um destes:

  • hbMemHeapSize - Corresponde ao tamanho total da memória de alocação dinâmica (heap) em bytes;
  • hbMemHeapFree - Corresponde a quantidade livre na memória de alocação dinâmica (heap);
  • hbMemHeapMax - Corresponde ao maior bloco de memória livre na memória de alocação dinêmica (heap)* em bytes;
  • hbMemStorageSize - Corresponde ao tamanho total da memória de armazenamento (storage) em bytes;
  • hbMemStorageFree - Corresponde a quantidade livre na memória de armazenamento (storage) em bytes.

* O Palm OS automaticamente efetua a desfragmentação da memória de alocação dinâmica quando necessário. Ao criarmos uma variável que ocupe um tamanho maior que o maior bloco de memória disponível, o Palm OS efetua a desfragmentação, na tentativa de conseguir um bloco de memória com o tamanho necessário para a variável.

Exemplo de utilização:

Memória Heap Disponível

Private Sub Button1_Click()
    "Mostra a quantidade de memória heap disponível
    MsgBox Format(MemInfo(hbMemHeapFree), "#.00") & " bytes livres."
End Sub

Garbage Collector

Enquanto a desfragmentação da memória é de responsabilidade do sistema operacional, a liberação de memória das variáveis que não estão mais sendo utilizadas na aplicação, é uma tarefa realizada por um serviço do Handheld Basic chamado Garbage Collector (ou Coletor de Lixo). O Garbage Collector, assim como em outras plataformas como Java e .NET Framework, é um serviço responsável em monitorar as variáveis criadas pela aplicação que não estão sendo utilizadas e efetuar a liberação de memória.

No caso do HB++, o Garbage Collector é uma rotina incorporada em cada arquivo .prc, que é executada de tempos em tempos, de acordo com um algoritmo interno do Handheld Basic. O Garbage Collector não é executado muitas vezes, para não prejudicar a performance da aplicação, mas normalmente é executado vezes suficientes para que as aplicações tenham sempre memória disponível. Dessa forma, não podemos prever exatamente quando o Garbage Collector será executado.

Como o sistema operacional Palm OS não é multi-tarefa, este serviço não é executado em paralelo, como acontece em Java e .NET Framework, então nossa aplicação deixa de funcionar por alguns milissegundos, até que o Garbage Collector termine sua execução. Entretanto, esta pausa na aplicação normalmente é imperceptível, pois para não prejudicar a performance da aplicação, o Garbage Collector normalmente é executado por apenas 10 milissegundos, liberando a memória que conseguir, neste tempo.

Embora não seja possível prever exatamente todas as vezes que o Garbage Collector será executado, existe uma regra no algoritmo em que a cada 64 alocações de memória, o Garbage Collector é executado. Antes disso ocorrer, existem três situações que podem causar a execução do Garbage Collector:

  • Quando existem blocos de memória que não estão sendo utilizados, e o usuário não efetuou nenhuma ação na aplicação por mais de 2 (dois) segundos (Idle time);
  • Quando o espaço disponível no heap é insuficiente para satisfazer uma nova alocação de memória;
  • Quando o Garbage Collector é executado explicitamente, através da função MemCompact.

A função MemCompact só deve ser utilizada em casos extremos, onde seja necessário alocar e liberar uma grande quantidade de memória, e o tamanho do heap seja limitado. Na maioria dos casos, não é necessário executar o Garbage Collector explicitamente, e devemos deixar o algoritmo decidir o melhor momento, para garantir a melhor performance possível para as aplicações.

A função MemCompact recebe dois parâmetros do tipo Boolean, que são, respectivamente:

  • bGarbageCollector - Define se o Garbage Collector deve ser executado (True ou False);
  • bCompact - Define se a memória deve ser Desfragmentada pelo sistema operacional (True ou False).

Exemplo de utilização:

Private Sub Button1_Click()
    "Executa o Garbage Collector e Desfragmenta a Memória
    MemCompact True, Tue
End Sub

Escopo de Variáveis

Toda variável possui um escopo que define onde ela pode ser utilizada, e qual seu tempo de vida. Variáveis criadas dentro de uma Function ou Sub, por exemplo, são válidas apenas dentro deste bloco, enquanto variáveis criadas dentro de classe, são válidas enquanto a instância da classe existir.

"Variavel criada no escopo da classe
Private m_VariavelDaClasse As String

Private Sub Button1_Click()
    "Variável criada no escopo do método
    Dim variavelDoMetodo As String

    "Acesso à variavel local (do método) "OK
    variavelDoMetodo   = "Botão 1"

    "Acesso à variavel da classe "OK
    m_VariavelDaClasse = "Botão 1"
End Sub

Private Sub Button2_Click()
    variavelDoMetodo   = "Botão 2" "ERRO DE COMPILAÇÃO
    m_VariavelDaClasse = "Botão 2" "OK
End Sub

No exemplo acima, ocorre o erro de compilação error #2201 : identifier "variavelDoMetodo" is not defined, indicando que a variável variavelDoMetodo não existe no método Button2_Click, e deveria ser declarada, assim como no método acima, utilizando a instrução Dim. Já o acesso à variavel m_VariavelDaClasse é garantido, pois todos os métodos de uma classe podem acessar as variáveis que estão no escopo da classe.

Escopo de Variáveis e o Garbage Collector

O Garbage Collector só libera a memória de variáveis que não estão sendo mais utilizadas pelas aplicações, e para fazer este controle, ele cria uma lista com todas as variáveis utilizadas, mapeadas aos endereços de memória onde estão alocados os objetos.

Tabela de Referência do Garbage Collector

"Variavel criada no escopo da classe
Private m_minhaTabela As tblMinhaTabela

Private Sub Button1_Click()
    Dim Empresa As String
    Empresa = "PDAExpert Tecnologia em Software"
    
    Dim Website As String
    WebSite = "http://www.pdaexpert.net"
    
    Dim Telefone As String
    Telefone = "+55 11 3374-3321"
    
    Set m_minhaTabela = New tblMinhaTabela
    m_minhaTabela.OpenTable hbModeReadWrite
    "(...)
    m_minhaTabela.Close
End Sub

No exemplo acima, quando a função Button1_Click termina a execução, todas as variáveis criadas dentro deste método deixam de existir, e a única variável que continua a existir é a m_minhaTabela, por estar no escopo da classe.

Tabela de Referência do Garbage Collector

Embora as variáveis que pertenciam ao método deixaram de existir, todos os objetos instanciados continuam a existir, e continuam a ocupar espaço em memória, até que o Garbage Collector seja executado e possa liberar a memória utilizada por estes objetos. No momento em que o Garbage Collector for executado, perceberá que os três primeiros objetos não possuem variáveis referenciando-os, e os removerá da memória.

Tabela de Referência do Garbage Collector

A importância de atribuir "Nothing" às variáveis

No exemplo acima, podemos notar que a variável m_minhaTabela continua a existir, por estar dentro do escopo da classe, mas podemos notar também que a tabela foi fechada, e que o objeto alocado na memória não será utilizado novamente na aplicação. Mesmo assim, a tabela continuará ocupando memória até que este formulário seja fechado, que será quando a variável m_minhaTabela deixará de existir e somente assim o Garbage Collector poderá liberar a memória ocupada pelo objeto.

Nestas situações, devemos atribuir Nothing à variável, para eliminarmos a referência que ela faz ao objeto, na tabela do Garbage Collector:

"Variavel criada no escopo da classe
Private m_minhaTabela As tblMinhaTabela

Private Sub Button1_Click()
    "(...)
    
    Set m_minhaTabela = New tblMinhaTabela
    m_minhaTabela.OpenTable hbModeReadWrite
    "(...)
    m_minhaTabela.Close
    Set m_minhaTabela = Nothing
End Sub

Tabela de Referência do Garbage Collector

Dessa forma, a variável m_minhaTabela continuará a existir, pois ainda está no escopo da classe, mas já não referencia o objeto alocado em memória, e assim permitirá que o Garbage Collector libere este bloco de memória, quando for executado.

Memória de Armazenamento

Diferentemente da memória de alocação dinâmica, a memória de armazenamento não possui um serviço como o Garbage Collector para liberação de memória. O funcionamento dessa memória é parecido com o funcionamento de um disco rígido de um computador, ou um pen drive, por exemplo. Quando instalamos aplicações e/ou inserimos registros em bancos de dados, o espaço vai sendo ocupado, e quando desejarmos liberar espaço, temos de apagar arquivos e/ou registros dos bancos de dados.

Histórico

  • 08/06/2007 - Primeira versão deste artigo.
Caio Proiete

Caio Proiete - Diretor Técnico da PDAExpert Tecnologia, empresa especializada no desenvolvimento de softwares para PDAs e telefones celulares nas plataformas Palm OS, Windows Mobile, Symbian OS e J2ME.
http://www.pdaexpert.net