Você está na página 1de 7

Gerenciamento de Memória nos Sistemas Operacionais

IOS e Android
Daniel Augusto de Souza Oliveira

Curso de Bacharelado em Sistemas de Informação – Universidade Federal do Piauí


(UFPI) – UAB União
União – PI– Brasil
danielsudaso@gmail.com

Abstract. This meta-article describes the memory management processes in


operating systems iOS and Android as well as its peculiarities and similarities,
advantages and errors that can occur in the memory management processes

Resumo. Este meta-artigo descreve os processos de gerenciamento de


memória nos Sistemas Operacionais IOS e Android bem como suas
particularidades e semelhanças, as vantagens e erros que podem ocorrer nos
processos de gerenciamento de memória.

1. Introdução
Gerenciamento de Memória é um complexo campo da ciência da computação e são
constantemente desenvolvidas várias técnicas para torná-la mais eficiente. A cada dia
que passa os programadores necessitam de mais memória e mais programas rodando
simultaneamente para poderem tratar cada vez mais informações. O tratamento
necessário da memória utilizada não é uma tarefa fácil de ser implementada. Existem
vários requisites que devem ser observados para o correto funcionamento, tais como,
segurança, isolamento, performance, entre outros. Para isto a função de gerenciar a
memória passa a ser do Sistema Operacional e não mais do aplicativo.
Para que uma memória funcione de maneira correta, é necessário que se tome cuidado
com vários elementos como segurança e isolamento, e para isso é utilizado o
gerenciamento de memória. Este desenvolve sua função a partir de duas tarefas, a
Alocação de Memória que pode ser tanto estática, feita quando o programa é compilado,
e a dinâmica, adiada até a execução; e a Fragmentação, desperdício de memória, por sua
vez pode ser interna, sobra na memória reservada ao programa, e externa que ocorre
quando após o termino dos programas são deixadas pequenas lacunas entre as páginas.
Para que a utilização da memória seja mais vantajosa, é utilizar a Paginação, processos
virtuais da memória, aplicados na divisão da memória física em partições menores,
chamadas de frames. O conjunto de registradores especiais rápidos chama-se
Translation Lookaside Buffer, estes são subdivididos em chave valor que lhe é dado em
todos os registradores ao mesmo tempo, e valor.
Existe uma técnica de gerenciamento de memória chamada memória virtual, que é onde
memórias principais e secundárias juntas criam a ilusão de que há muito mais memória,
com isso os programas e suas estruturas de dados não se limitam ao tamanho da
memória física, e assumem endereços na memória secundária. O gerenciamento de
memória virtual pode ocasionar vazamento de memória, ou seja, quando determinada
quantia de memória é alocada e não liberada mesmo que não sendo utilizada assim
dados perdem a referência sem ao menos terem usado a memória. O gerenciamento
automático chama-se Garbage colletor, ele retira os blocos de memória
automaticamente. Seus algoritmos são divididos em duas famílias: a identificação
direta, por contagem de referência, e a identificação indireta, pelo varrimento.
O sistema deve, portanto, assegurar que cada processo tenha seu próprio espaço na
memória, prover a proteção deste espaço para que não haja a sobrescrição e utilização
por outro processo e possibilitar que uma aplicação não utilize mais memória que a
existente fisicamente.

2. Gerenciamento de Memória no Sistema Operacional IOS


O gerenciamento de memória de um determinado aplicativo no IOS, é o processo de
alocação de memória durante a execução do seu programa, utilizando-o e em seguida
liberando quando terminado. Um código bem escrito/compilado usa o mínimo de
memória possível. Na linguagem Objective C, o programa pode também ser visto como
uma forma de distribuir a propriedade de recursos de memória limitados entre pedaços
de dados e de códigos.
Embora o gerenciamento de memória é normalmente considerado ao nível de um objeto
individual, seu objetivo é realmente gerenciar gráficos de determinados objetos.
Todavia, o usuário tem que ter certeza de que o mesmo não tem mais objetos na
memória do que realmente precisa.

Figura 1: Gerenciamento de Memória. Fonte: developer.appe.com/library/ios


Continuando o assunto, é relevante citar que o Objective-C fornece três métodos de
gerenciamento de memória de aplicativo.
1 – O método conhecido como “Manual Reter-Release” ou MRR, onde o usuário pode
explicitamente gerenciar a memória, mantendo o controle de objetos que o mesmo
possui. Isto é implementado utilizando um modelo, conhecido como a contagem de
referência, que a base de classe NSObject fornece em conjunto com o ambiente de
execução.
2 – Em um Contador automático de referência (Automatic Reference Counting, ou
ARC), o sistema usa o mesmo sistema de contador de referência que o MRR, mas insere
as chamadas de métodos gerenciamento de memórias apropriando-as para o usuário em
tempo de compilação.
3 – No coletor de lixos, o sistema automaticamente acompanha quais objetos pertencem
as quais outros objetos. Isso, então, automaticamente livra (ou coleta o lixo) os objetos
que não estão referenciados. Ele usa um mecanismo diferente daquele empregado no
MRR ou ARC e é apenas suportado no ambiente de execução do MAC OS X, não no
IOS.
Se um usuário pensa em escrever um código para o IOS, o mesmo deve usar o
gerenciamento de memória explícita, além disso, estas técnicas de gerenciamento de
memória devem ser consideradas ao se escrever bibliotecas de rotinas, plug-ins, ou
código compartilhado tanto em processos coletores de lixo como processos não
coletores de lixo.

2.1. Boas práticas para prevenção de problemas de memórias relacionadas.


Existem dois tipos principais de problemas ou que resultam num gerenciamento
incorreto de memória, são esses:
1 – Liberação ou substituição de dados que ainda estão em uso.
Isso faz com que a decomposição de memória, normalmente, resulte em falha do
aplicativo, ou ainda pior, que os dados do usuário sejam corrompidos.
2 – A não liberação dos dados que estão em desuso faz com que ocorram vazamento de
memória.
Um vazamento é onde a memória alocada não é liberada, mesmo que ela não torne a ser
utilizada novamente. Vazamentos que fazem com que o aplicativo use quantidades cada
vez maiores de memória, que pode resultar num péssimo desempenho do sistema
operacional.
Pensando em gerenciamento de memória a partir da perspectiva de contagem de
referência, que, no entanto, é frequentemente contraditório, porque o usuário tende a
considerar o gerenciamento de memória em termos de detalhes de implementação, em
vez de em termos de seus objetivos reais. Logo, o usuário deve pensar em
gerenciamento de memória a partir da perspectiva da propriedade de objeto e gráficos de
objetos.
O Cocoa utiliza uma convenção de nomeação direta para indicar quando o usuário
possui um objeto retornado por um método.
Embora a política de base seja simples, existem algumas medidas práticas que o usuário
pode tomar para tornar mais fácil a gestão ou controle da memória, para ajudar a
garantir que o programa permaneça confiável e robusto ao mesmo tempo em que são
minimizados os requisitos de seus recursos.
2.2. Uso de ferramentas de análise para depurar problemas de memória.
Para identificar problemas com o seu código em tempo de compilação, o usuário pode
usar o “Clang Static Analyzer” que está embutido no Xcode.
Se os problemas de gerenciamento de memória que, no entanto, surgem, existem outras
ferramentas e técnicas que o usuário pode usar para identificar e diagnosticar os
problemas.
1 – Muitas das ferramentas e técnicas são descritas em Nota Técnica TN2239, iOS
Debugging Magic e em particular, o uso de NSZombie para ajudar a encontrar o objeto.
2 – O usuário pode usar instrumentos para acompanhar os eventos de contagem de
referência e procurar vazamentos de memória.

3. Gerenciamento de memória no Sistema Operacional Android

O Android é um sistema operacional baseado no kernel do Linux, porém, existe pouca


coisa em comum com distribuições Linux convencionais (embarcadas ou não),
lembrando que um sistema embarcado, ou embutido é um sistema microprocessado no
qual o computador é completamente encapsulado ou dedicado ao dispositivo ou sistema
que ele controla. À grosso modo, o Android é uma máquina virtual Java rodando sobre
o kernel do Linux, dando suporte para o desenvolvimento de aplicações Java através de
um conjunto de bibliotecas e serviços. Todas as operações básicas do sistema
operacional em níveis mais baixos, como o I/O, gerenciamento de memória, e assim por
diante, são tratados pelo kernel do Linux. Sendo assim o sistema se utiliza da biblioteca
padrão do C, que acompanha o Linux há anos.

3.1. Android runtime


Existem algumas peculiaridades no gerenciamento de memória do Android. O Android
inclui um grupo de bibliotecas da linguagem Java. Toda aplicação Android possui um
processo e instância próprios na máquina virtual Dalvik. Assim como nas plataformas
Java e .Net. O Android utiliza-se de uma Máquina Virtual (VM) própria, para assegurar
que a execução de várias instâncias em um único dispositivo seja eficiente. A Dalvik
utiliza o kernel do Linux para lidar com funcionalidades de nível mais baixo, como
segurança, threading e gerenciamento de processos e memória.

3.2. Dalvik Virtual Machine


Todo hardware do dispositivo e serviços do sistema operacional são controlados usando
o Dalvik como uma camada intermediária. Através do uso desta máquina virtual para
hospedar a execução de aplicativos os desenvolvedores podem abstrair da
implementação em hardwares em particular, facilitando a criação de novos aplicativos,
que podem executar em qualquer dispositivo igualmente.
A Dalvik possui também uma extensão própria para os arquivos executáveis, a .dex, que
garante um menor consumo de memória. Os arquivos .dex são criados através da
transformação das classes compiladas do Java pelas ferramentas fornecidas na Android
SDK.
Além disso, desde a versão 2.2(Froyo), o Android possui uma implementação de Just-
in-time (JIT), que compila dexcodes para a arquitetura-alvo em tempo de execução,
tornando a execução dos processos consideravelmente mais rápidas, já que não precisa
ficar interpretando dexcodes.
Diferentemente da Java VM e .Net, o Android também gerencia o tempo de vida do
processo, para otimizar o uso de memória verifica-se o estado de resposta do aplicativo,
parando e matando processos conforme necessário para liberar recursos a aplicações de
maior prioridade.
Junto com a máquina virtual Dalvik, o Android usa o framework Apache Harmony,
desenvolvido pela Apache Software Fundation como biblioteca padrão de classes Java.

3.3. Prioridade e status de processos


No Android todos os processos são mantidos na memória até que haja a necessidade de
recursos para outros processos. A ordem na qual os processos são finalizados para
liberação de recursos está associada ao nível de prioridade da aplicação do mesmo. A
prioridade de uma aplicação é igual a de seu componente de maior prioridade.

3.4. Memória Virtual


Memória Virtual é uma técnica que usa a memória principal como uma cache para
armazenamento secundário. Houve duas motivações principais: permitir o
compartilhamento seguro e eficiente da memória entre vários programas e remover os
transtornos de programação de uma quantidade pequena e limitada na memória
principal. A memória virtual consiste em recursos de hardware e software com três
funções básicas:
1- Realocação: Que assegura que cada processo (aplicação) tenha o seu próprio
espaço de endereçamento, começando em zero;
2- Proteção: Impede que um processo utilize um endereço de memória que não lhe
pertença;
3- Paginação (paging) ou troca (swapping): Que possibilita a uma aplicação utilizar
mais memória do que a fisicamente existente.
Simplificadamente, um usuário ou programador vê um espaço de endereçamento virtual,
que pode ser igual, maior ou menor que a memória física.
O gerenciamento de memória em baixo nível do Android é feito pelo Linux Kernel 2.6.
A descrição da memória virtual é feita, portanto no Linux através de segmentação e
paginação.

3.4.1. Segmentação
A Segmentação divide a memória em dois espaços distintos, o espaço do kernel (Kernel
Space) e o espaço do usuário (User Space). Dentro destes espaços temos os quatro
segmentos básicos:
 Kernel Code.
 Kernel Data/Stack
 User Code
 User Data/Stack
Desta forma podemos garantir a proteção da memória, evitando o acesso de memória
entre usuários. Garantindo principalmente que processos em modo kernel não se
misturem com processos em modo usuário e que a pilha de dados não cresça
indiscriminadamente.

3.4.2. Paginação
Somente com a segmentação teríamos blocos de memória contínuos para cada processo.
Isso sobrecarregaria a memória, copiando a imagem de um processo todo de uma vez.
Caso não haja um espaço de memória suficiente para alocar todo o processo, ou seja,
não há um segmento que o comportasse e haverá falha por falta de segmento
(segmentation fault). Para resolver tal problema é usada a paginação, onde a memória é
dividida em pedaços de tamanho fixo (páginas), e segmentos de código são alocados
nestes e mapeados utilizando-se uma tabela de páginas, ao invés de alocação de todo
código de uma única vez.
No Linux a paginação é feita em três níveis, ou seja são usadas três tabelas para mapear
a memória, a Page Directory, Page Middle Directory e Page Table. O campo directory
field é usado como índice para o diretório global, que existe em cada processo. O valor
achado nessa posição é um ponteiro para a page middle table, que é novamente indexada
e contém um ponteiro que indica para o endereço virtual de memória. Além disso, para
aumentar o desempenho do sistema é mantido um buffer com os últimos endereços
acessados, para que não haja necessidade de fazer múltiplos acessos as páginas. Este
buffer é chamado de TLB (Translation Lookaside Buffer). Logo antes de realizar a
procura do endereço utilizando as tabelas de página, o sistema busca a tradução direta
do endereço no TLB. É importante que a TLB se mantenha atualizada com relação ao
conteúdo da memória principal. Como a TLB, em geral, possui tamanho menor que a
memória principal, é necessário substituir alguma tradução armazenada por outra mais
recente.

Figura 2: Paginação no Linux. Fonte:


http://www.ces33.wikidot.com/gerenciamentodememoria:lutyigoraquino
3.4.3. Substituição de páginas
Quando a memória já preencheu todas as páginas possíveis é necessário realizar a
substituição de páginas quando o processo em execução requisita que uma nova página
seja alocada. Para tal existem diversos algoritmos de substituição para escolher qual
página deverá ser removida para a entrada da nova página na memória.
Alguns algoritmos foram criados para otimizar esta substituição. O algoritmo utilizado
no Linux é o Last Recentment Used (LRU).
O LRU remove da memória a página que não foi utilizada por mais tempo. Isso baseia-
se na suposição de que páginas que não foram recentemente utilizadas também não o
serão nas próximas instruções, enquanto páginas bastante utilizadas agora tendem a ser
bastante utilizadas nas instruções seguintes.

3.5. Comunicação entre processos


Como a memória é segmentada e paginada, o acesso de um processo à memória que não
lhe pertence não é permitido. Por isso foram criados mecanismos de comunicação entre
processos. Para os sistemas Linux tradicionais usa-se o padrão System V IPC para
comunicação entre processos (message queues, semáforos e shared memory). Já no
Android usa-se o binder para a comunicação entre processos. Ele implemente um
módulo no kernel em “drivers/misc/binder.c” para esta tarefa. Toda comunicação entre
processos no Android passa pelo binder. Para o desenvolvedor de aplicações Android, o
processo é transparente, já que é abstraído pelas bibliotecas do sistema.

6. Referências
Gerenciamento de Memória. Disponível:
http://pt.wikipedia.org/wiki/Gerenciamento_de_memória Acesso: julho/ 2016.
Gerenciamento de Memória. Disponível:
http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedi
a-CocoaCore/MemoryManagement.html Acesso: julho/2016.
Mobworld – “Memory Management in Android”. Disponível:
http://mobworld.wordpress.com/2010/07/05/memory-management-in-android
Acesso: julho/2016.

Você também pode gostar