Como posso encontrar a memória usada no meu aplicativo Android programaticamente?
Espero que haja uma maneira de fazer isso. Além disso, como obtenho também a memória livre do telefone?
Como posso encontrar a memória usada no meu aplicativo Android programaticamente?
Espero que haja uma maneira de fazer isso. Além disso, como obtenho também a memória livre do telefone?
Respostas:
Observe que o uso de memória em sistemas operacionais modernos como o Linux é uma área extremamente complicada e difícil de entender. De fato, as chances de você realmente interpretar corretamente quaisquer números que conseguir são extremamente baixas. (Praticamente toda vez que olho para os números de uso de memória com outros engenheiros, sempre há uma longa discussão sobre o que eles realmente significam que só resultam em uma vaga conclusão.)
Nota: agora temos uma documentação muito mais extensa sobre o gerenciamento da memória do seu aplicativo, que abrange grande parte do material aqui e está mais atualizado com o estado do Android.
A primeira coisa é provavelmente ler a última parte deste artigo, que discute como a memória é gerenciada no Android:
Alterações na API de serviço iniciadas no Android 2.0
Agora ActivityManager.getMemoryInfo()
é a nossa API de nível mais alto para analisar o uso geral da memória. Isso está presente principalmente para ajudar um aplicativo a avaliar o quão perto o sistema está de não ter mais memória para processos em segundo plano, necessitando, assim, começar a eliminar processos necessários, como serviços. Para aplicativos Java puros, isso deve ser de pouca utilidade, pois o limite de heap Java existe em parte para evitar que um aplicativo consiga forçar o sistema até esse ponto.
No nível inferior, você pode usar a API Debug para obter informações brutas no nível do kernel sobre o uso da memória: android.os.Debug.MemoryInfo
Observe que, começando com 2.0, também há uma API, ActivityManager.getProcessMemoryInfo
para obter essas informações sobre outro processo: ActivityManager.getProcessMemoryInfo (int [])
Isso retorna uma estrutura MemoryInfo de baixo nível com todos esses dados:
/** The proportional set size for dalvik. */
public int dalvikPss;
/** The private dirty pages used by dalvik. */
public int dalvikPrivateDirty;
/** The shared dirty pages used by dalvik. */
public int dalvikSharedDirty;
/** The proportional set size for the native heap. */
public int nativePss;
/** The private dirty pages used by the native heap. */
public int nativePrivateDirty;
/** The shared dirty pages used by the native heap. */
public int nativeSharedDirty;
/** The proportional set size for everything else. */
public int otherPss;
/** The private dirty pages used by everything else. */
public int otherPrivateDirty;
/** The shared dirty pages used by everything else. */
public int otherSharedDirty;
Mas, como o que é a diferença entre Pss
, PrivateDirty
e SharedDirty
... bem, agora a diversão começa.
Muita memória no Android (e sistemas Linux em geral) é realmente compartilhada entre vários processos. Portanto, quanta memória um processo usa não é realmente clara. Adicione ainda mais a paginação em disco (sem falar na troca que não usamos no Android) e fica ainda menos claro.
Portanto, se você pegar toda a RAM física realmente mapeada para cada processo e adicionar todos os processos, provavelmente terminará com um número muito maior que a RAM total real.
O Pss
número é uma métrica que o kernel calcula que leva em consideração o compartilhamento de memória - basicamente cada página de RAM em um processo é dimensionada por uma proporção do número de outros processos que também usam essa página. Dessa forma, você pode (em teoria) adicionar os pss em todos os processos para ver a RAM total que eles estão usando e comparar pss entre processos para obter uma idéia aproximada de seu peso relativo.
A outra métrica interessante aqui é PrivateDirty
, que é basicamente a quantidade de RAM dentro do processo que não pode ser paginada em disco (não é suportada pelos mesmos dados em disco) e não é compartilhada com outros processos. Outra maneira de olhar para isso é a RAM que ficará disponível para o sistema quando esse processo for encerrado (e provavelmente submergiu rapidamente em caches e outros usos).
Essas são as APIs do SDK para isso. No entanto, há mais que você pode fazer como desenvolvedor com seu dispositivo.
Usando adb
, há muitas informações que você pode obter sobre o uso de memória de um sistema em execução. Um comando comum é o comando adb shell dumpsys meminfo
que fornecerá várias informações sobre o uso da memória de cada processo Java, contendo as informações acima e várias outras coisas. Você também pode usar o nome ou o pid de um único processo para ver, por exemplo, adb shell dumpsys meminfo system
me dê o processo do sistema:
** MEMINFO no pid 890 [sistema] ** dalvik nativo outro total tamanho: 10940 7047 N / A 17987 atribuído: 8943 5516 N / A 14459 grátis: 336 1531 N / A 1867 (Pss): 4585 9282 11916 25783 (compartilhado sujo): 2184 3596 916 6696 (priv sujo): 4504 5956 7456 17916 Objetos Visualizações: 149 AppContexts: 13 Atividades: 0 Ativos: 4 Ativos gerentes: 4 Fichários locais: 141 Fichários proxy: 158 Destinatários da Morte: 49 Soquetes OpenSSL: 0 SQL heap: 205 dbArquivos: 0 numPagers: 0 inactivePageKB: 0 activePageKB: 0
A seção superior é a principal, onde size
é o tamanho total no espaço de endereço de um heap específico, allocated
é o kb de alocações reais que o heap pensa que possui, free
é o kb restante livre do heap para alocações adicionais pss
e priv dirty
são os mesmos conforme discutido anteriormente, específico para páginas associadas a cada um dos montes.
Se você só quer olhar para o uso de memória em todos os processos, você pode usar o comando adb shell procrank
. A saída disso no mesmo sistema é semelhante a:
PID Vss Rss Pss Uss cmdline 890 84456K 48668K 25850K 21284K system_server 1231 50748K 39088K 17587K 13792K com.android.launcher2 947 34488K 28528K 10834K 9308K com.android.wallpaper 987 26964K 26956K 8751K 7308K com.google.process.gapps 954 24300K 24296K 6249K 4824K com.android.phone 948 23020K 23016K 5864K 4748K com.android.inputmethod.latin 888 25728K 25724K 5774K 3668K zigoto 977 24100K 24096K 5667K 4340K android.process.acore ... 59 336K 332K 99K 92K / sistema / bin / installd 60 396K 392K 93K 84K / sistema / bin / keystore 51 280K 276K 74K 68K / sistema / bin / servicemanager 54 256K 252K 69K 64K / sistema / bin / depuradord
Aqui, as colunas Vss
e Rss
são basicamente ruídos (esses são o espaço de endereço direto e o uso de RAM de um processo, onde se você adicionar o uso de RAM nos processos, obtém um número ridiculamente grande).
Pss
é como vimos antes, e Uss
é Priv Dirty
.
É interessante notar aqui: Pss
e Uss
são um pouco (ou mais que um pouco) diferentes do que vimos meminfo
. Por que é que? Bem, procrank usa um mecanismo de kernel diferente para coletar seus dados do que meminfo
faz, e eles fornecem resultados ligeiramente diferentes. Por que é que? Honestamente, eu não tenho idéia. Eu acredito que procrank
pode ser o mais preciso ... mas, na verdade, isso deixa o ponto: "pegue qualquer informação de memória obtida com um grão de sal; geralmente um grão muito grande".
Finalmente, há o comando adb shell cat /proc/meminfo
que fornece um resumo do uso geral de memória do sistema. Há muitos dados aqui, apenas os primeiros números que vale a pena discutir (e os demais são entendidos por poucas pessoas, e minhas perguntas sobre essas poucas pessoas sobre eles geralmente resultam em explicações conflitantes):
Total de mem: 395144 kB MemFree: 184936 kB Tampões: 880 kB Em cache: 84104 kB SwapCached: 0 kB
MemTotal
é a quantidade total de memória disponível para o kernel e o espaço do usuário (geralmente menor que a RAM física real do dispositivo, pois parte dessa RAM é necessária para o rádio, os buffers de DMA, etc.).
MemFree
é a quantidade de RAM que não está sendo usada. O número que você vê aqui é muito alto; normalmente em um sistema Android, isso teria apenas alguns MB, já que tentamos usar a memória disponível para manter os processos em execução
Cached
é a RAM que está sendo usada para caches do sistema de arquivos e outras coisas. Os sistemas típicos precisarão ter aproximadamente 20 MB para evitar problemas de paginação; o assassino de falta de memória do Android é ajustado para um sistema específico para garantir que os processos em segundo plano sejam eliminados antes que a RAM em cache seja consumida demais por eles para resultar em tal paginação.
Sim, você pode obter informações de memória programaticamente e decidir se deve fazer um trabalho intensivo de memória.
Obtenha o tamanho de heap da VM ligando para:
Runtime.getRuntime().totalMemory();
Obtenha memória VM alocada chamando:
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
Obtenha o Limite de tamanho de heap da VM chamando:
Runtime.getRuntime().maxMemory()
Obter memória alocada nativa chamando:
Debug.getNativeHeapAllocatedSize();
Eu criei um aplicativo para descobrir o comportamento do OutOfMemoryError e monitorar o uso da memória.
https://play.google.com/store/apps/details?id=net.coocood.oomresearch
Você pode obter o código fonte em https://github.com/coocood/oom-research
Este é um trabalho em andamento, mas é isso que eu não entendo:
ActivityManager activityManager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
Log.i(TAG, " memoryInfo.availMem " + memoryInfo.availMem + "\n" );
Log.i(TAG, " memoryInfo.lowMemory " + memoryInfo.lowMemory + "\n" );
Log.i(TAG, " memoryInfo.threshold " + memoryInfo.threshold + "\n" );
List<RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();
Map<Integer, String> pidMap = new TreeMap<Integer, String>();
for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses)
{
pidMap.put(runningAppProcessInfo.pid, runningAppProcessInfo.processName);
}
Collection<Integer> keys = pidMap.keySet();
for(int key : keys)
{
int pids[] = new int[1];
pids[0] = key;
android.os.Debug.MemoryInfo[] memoryInfoArray = activityManager.getProcessMemoryInfo(pids);
for(android.os.Debug.MemoryInfo pidMemoryInfo: memoryInfoArray)
{
Log.i(TAG, String.format("** MEMINFO in pid %d [%s] **\n",pids[0],pidMap.get(pids[0])));
Log.i(TAG, " pidMemoryInfo.getTotalPrivateDirty(): " + pidMemoryInfo.getTotalPrivateDirty() + "\n");
Log.i(TAG, " pidMemoryInfo.getTotalPss(): " + pidMemoryInfo.getTotalPss() + "\n");
Log.i(TAG, " pidMemoryInfo.getTotalSharedDirty(): " + pidMemoryInfo.getTotalSharedDirty() + "\n");
}
}
Por que o PID não é mapeado para o resultado em activityManager.getProcessMemoryInfo ()? Claramente, você deseja tornar os dados resultantes significativos. Por que o Google tornou tão difícil correlacionar os resultados? O sistema atual nem funciona bem se eu quiser processar todo o uso da memória, pois o resultado retornado é uma matriz de objetos android.os.Debug.MemoryInfo, mas nenhum desses objetos realmente diz a que pids eles estão associados. Se você simplesmente passar uma série de todos os pids, não terá como entender os resultados. Pelo que entendi, é inútil passar mais de um pid de cada vez e, se for esse o caso, por que fazê-lo para que activityManager.getProcessMemoryInfo () use apenas uma matriz int?
O Hackbod's é uma das melhores respostas no Stack Overflow. Lança luz sobre um assunto muito obscuro. Isto me ajudou bastante.
Outro recurso realmente útil é este vídeo imperdível: Google I / O 2011: Gerenciamento de memória para aplicativos Android
ATUALIZAR:
Process Stats, um serviço para descobrir como o aplicativo gerencia a memória explicada na postagem do blog Stats do Processo: Entendendo como o aplicativo usa a RAM de Dianne Hackborn:
O Android Studio 0.8.10+ introduziu uma ferramenta incrivelmente útil chamada Monitor de Memória .
Para que serve:
- Mostrando memória disponível e usada em um gráfico e eventos de coleta de lixo ao longo do tempo.
- Testando rapidamente se a lentidão do aplicativo pode estar relacionada a eventos excessivos de coleta de lixo.
- Testar rapidamente se as falhas do aplicativo podem estar relacionadas à falta de memória.
Figura 1. Forçando um evento de GC (Garbage Collection) no Android Memory Monitor
Você pode obter muitas informações boas sobre o consumo de RAM em tempo real do seu aplicativo usando-o.
1) Acho que não, pelo menos não do Java.
2)
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);
Descobrimos que todas as formas padrão de obter a memória total do processo atual têm alguns problemas.
Runtime.getRuntime().totalMemory()
: retorna apenas memória JVMActivityManager.getMemoryInfo()
, Process.getFreeMemory()
E qualquer outra coisa com base em /proc/meminfo
- retorna informação de memória sobre todos os processos combinados (por exemplo android_util_Process.cpp )Debug.getNativeHeapAllocatedSize()
- usos mallinfo()
que retornam informações sobre alocações de memória executadas apenas por malloc()
e funções relacionadas (consulte android_os_Debug.cpp )Debug.getMemoryInfo()
- faz o trabalho, mas é muito lento. Demora cerca de 200 ms no Nexus 6 para uma única chamada. A sobrecarga de desempenho torna essa função inútil para nós, como a chamamos regularmente e todas as chamadas são bastante visíveis (consulte android_os_Debug.cpp )ActivityManager.getProcessMemoryInfo(int[])
- chama Debug.getMemoryInfo()
internamente (consulte ActivityManagerService.java )Por fim, acabamos usando o seguinte código:
const long pageSize = 4 * 1024; //`sysconf(_SC_PAGESIZE)`
string stats = File.ReadAllText("/proc/self/statm");
var statsArr = stats.Split(new [] {' ', '\t', '\n'}, 3);
if( statsArr.Length < 2 )
throw new Exception("Parsing error of /proc/self/statm: " + stats);
return long.Parse(statsArr[1]) * pageSize;
Retorna a métrica VmRSS . Você pode encontrar mais detalhes sobre isso aqui: um , dois e três .
PS : Observei que o tema ainda não possui um trecho de código real e simples de como estimar o uso de memória privada do processo, se o desempenho não for um requisito crítico:
Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(memInfo);
long res = memInfo.getTotalPrivateDirty();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
res += memInfo.getTotalPrivateClean();
return res * 1024L;
No android studio 3.0, eles introduziram o android-profiler para ajudar você a entender como o aplicativo usa os recursos de CPU, memória, rede e bateria.
https://developer.android.com/studio/profile/android-profiler
Há muitas respostas acima que definitivamente o ajudarão, mas (após 2 dias de recursos e pesquisas em ferramentas de memória adb) , acho que também posso ajudar com minha opinião .
Como Hackbod diz: Portanto, se você pegasse toda a RAM física realmente mapeada para cada processo e adicione todos os processos, provavelmente terminaria com um número muito maior que a RAM total real. portanto, não há como obter quantidade exata de memória por processo.
Mas você pode se aproximar disso por alguma lógica .. e eu direi como ..
Há alguns API como
android.os.Debug.MemoryInfo
eActivityManager.getMemoryInfo()
mencionado acima, que você já deve ter sido lido sobre e usado, mas vou falar sobre outra maneira
Então, primeiro você precisa ser um usuário root para fazer funcionar. Entre no console com privilégio root executando su
no processo e obtenha seuoutput and input stream
. Em seguida, passe id\n
(enter) no fluxo de saída e escreva-o para processar a saída. Se obter um fluxo de entrada contendo uid=0
, você é usuário root.
Agora, aqui está a lógica que você usará no processo acima
Quando você obtém ouputstream do processo, você comando (procrank, dumpsys meminfo etc ...) com\n
vez de id e obtenha inputstream
e leia, armazene o fluxo em bytes [], char [] etc. use dados brutos .. e você está feito !!!!!
permissão:
<uses-permission android:name="android.permission.FACTORY_TEST"/>
Verifique se você é usuário root:
// su command to get root access
Process process = Runtime.getRuntime().exec("su");
DataOutputStream dataOutputStream =
new DataOutputStream(process.getOutputStream());
DataInputStream dataInputStream =
new DataInputStream(process.getInputStream());
if (dataInputStream != null && dataOutputStream != null) {
// write id to console with enter
dataOutputStream.writeBytes("id\n");
dataOutputStream.flush();
String Uid = dataInputStream.readLine();
// read output and check if uid is there
if (Uid.contains("uid=0")) {
// you are root user
}
}
Execute seu comando com su
Process process = Runtime.getRuntime().exec("su");
DataOutputStream dataOutputStream =
new DataOutputStream(process.getOutputStream());
if (dataOutputStream != null) {
// adb command
dataOutputStream.writeBytes("procrank\n");
dataOutputStream.flush();
BufferedInputStream bufferedInputStream =
new BufferedInputStream(process.getInputStream());
// this is important as it takes times to return to next line so wait
// else you with get empty bytes in buffered stream
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// read buffered stream into byte,char etc.
byte[] bff = new byte[bufferedInputStream.available()];
bufferedInputStream.read(bff);
bufferedInputStream.close();
}
}
Você obtém dados brutos em uma única cadeia de caracteres do console, em vez de em qualquer instância de qualquer API, que é complexa para armazenar, pois você precisará separá-los manualmente .
Esta é apenas uma tentativa, por favor, sugira-me se eu perdi alguma coisa