Condividi tramite


Risolvere i problemi di prestazioni della memoria in Linux

Si applica a: ✔️ macchine virtuali di Linux

Questo articolo illustra come risolvere i problemi di prestazioni della memoria che si verificano in macchine virtuali Linux in Microsoft Azure.

Il primo passaggio per lavorare sui problemi relativi alla memoria consiste nel valutare gli elementi seguenti:

  • Modalità di utilizzo della memoria da parte delle applicazioni ospitate nel sistema
  • Livello appropriato di memoria disponibile nel sistema

È possibile iniziare analizzando i modelli di carico di lavoro per determinare se il sistema è configurato correttamente. Successivamente, potrebbe essere necessario valutare la possibilità di ridimensionare la macchina virtuale o di scegliere tra un'architettura NUMA (Accesso non uniforme alla memoria) e l'architettura UMA (Uniform Memory Access). Inoltre, è utile considerare se le prestazioni dell'applicazione potrebbero trarre vantaggio da Transparent HugePages (THP). L'approccio migliore consiste nel collaborare con il fornitore dell'applicazione per comprendere i requisiti di memoria consigliati.

Aree chiave interessate dalla memoria

  • Allocazione di memoria del processo : la memoria è una risorsa necessaria per ogni processo, incluso il kernel. La quantità di memoria necessaria dipende dalla progettazione e dallo scopo del processo. La memoria è solitamente assegnata allo stack o all'heap. Ad esempio, i database in memoria, ad esempio SAP HANA, si basano principalmente sulla memoria per archiviare ed elaborare i dati in modo efficiente.

  • Utilizzo cache pagine: la memoria può anche essere utilizzata indirettamente tramite un aumento della cache delle pagine. La cache delle pagine è una rappresentazione in memoria di un file letto in precedenza da un disco. Questa cache consente di evitare letture su disco ripetute. L'esempio migliore di questo processo è un file server che trae vantaggio da questa funzionalità del kernel sottostante.

  • Architettura della memoria : è importante sapere quali applicazioni o applicazioni sono in esecuzione nella stessa macchina virtuale e se potrebbero competere per la memoria disponibile. Potrebbe anche essere necessario verificare se la macchina virtuale è configurata per l'uso dell'architettura NUMA o UMA. A seconda dei requisiti di memoria di un processo, l'architettura UMA potrebbe essere preferibile in modo che la RAM completa possa essere risolta senza penalità. D'altra parte, per HPC (High Performance Computing) che coinvolge molti processi o processi di piccole dimensioni che rientrano in uno dei nodi NUMA, è possibile trarre vantaggio dalla località della cache della CPU.

  • Sovrallocazione della memoria - È anche importante determinare se il kernel consente la sovrallocazione della memoria. A seconda della configurazione, ogni richiesta di memoria viene soddisfatta fino a quando l'importo richiesto non è più disponibile.

  • Scambia spazio L'abilitazione dello scambio migliora la stabilità complessiva del sistema fornendo un buffer durante condizioni di memoria insufficiente. Questo buffer aiuta il sistema a rimanere resiliente sotto pressione. Per altre informazioni, vedere questo articolo sul kernel Linux.

Informazioni sugli strumenti per la risoluzione dei problemi relativi alla memoria

Per risolvere i problemi, è possibile usare gli strumenti da riga di comando seguenti.

gratuito

Per visualizzare la quantità di memoria disponibile e usata in un sistema, usare il free comando .

Screenshot dell'output gratuito di esempio.

Questo comando genera un riepilogo della memoria riservata e disponibile, incluso lo spazio di scambio totale e usato.

pidstat e vmstat

Per una visualizzazione più dettagliata dell'utilizzo della memoria da parte dei singoli processi, usare il pidstat -r comando .

Screenshot dell'output di esempio pidstat -r.

Quando si analizzano i report sull'utilizzo della memoria, due colonne importanti da osservare sono VSZ e RSS:

  • VSZ (Virtual Set Size) mostra la quantità totale di memoria virtuale (in kilobyte) riservata da un processo.
  • RSS (Resident Set Size) indica la quantità di memoria virtuale attualmente contenuta nella RAM (ad esempio, memoria di cui è stato eseguito il commit).

Un'altra metrica utile è majflt/s (il numero di errori di pagina principali al secondo). Questo numero misura la frequenza con cui una pagina di memoria deve essere letta da un dispositivo di scambio. In caso di dubbi sull'utilizzo elevato della memoria di scambio, verificare la quantità utilizzando lo strumento vmstat per monitorare nel tempo le statistiche di page-in e page-out.

Esempio di output di vmstatvmstat

In questo esempio è possibile osservare che molte pagine di memoria vengono lette o scritte per lo scambio. Questi valori elevati indicano in genere che il sistema sta esaurendo la memoria disponibile. Questa condizione può verificarsi perché più processi sono in competizione per la memoria o perché la maggior parte delle applicazioni non può usare la memoria disponibile.

Un motivo comune per cui la memoria non disponibile è l'uso di HugePages. HugePages sono memoria riservata. Non tutte le applicazioni possono usare memoria riservata. In alcune situazioni, potrebbe essere necessario valutare se le applicazioni necessitano di HugePages o funzionano in modo più efficace usando Transparent HugePages (THP). THP consente al kernel di gestire in modo dinamico pagine di memoria di grandi dimensioni. Ad esempio, la macchina virtuale Java (JVM) può sfruttare il THP abilitando il flag seguente:

-XX:+UseTransparentHugePages

Per altre informazioni su THP, vedere Transparent HugePage Support.

Per altre informazioni su HugePages, vedere HugeTLB Pages.For more information about HugePages, see HugeTLB Pages.

Test dell'utilizzo THP in un programma di esempio

Per osservare come il THP viene usato dal sistema, è possibile eseguire un piccolo programma C che alloca circa 256 MB di RAM. Il programma usa la madvise chiamata di sistema per indicare al kernel Linux che questa area di memoria deve usare pagine enormi se il THP è supportato.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>

#define LARGE_MEMORY_SIZE (256 * 1024 * 1024) // 256MB

int main() {
    char str[2];

    // Allocate a large memory area
    void *addr = mmap(NULL, LARGE_MEMORY_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);


    if (addr == MAP_FAILED) {
        perror("mmap");
        return 1;
    }


    // Use madvise to give advice about the memory usage
    if (madvise(addr, LARGE_MEMORY_SIZE, MADV_HUGEPAGE) != 0) {
        perror("madvise");
        munmap(addr, LARGE_MEMORY_SIZE);
        return 1;
    }

    // Initialize the memory
    int *array = (int *)addr;
    for (int i = 0; i < LARGE_MEMORY_SIZE / sizeof(int); i++) {
        array[i] = i;
    }

    memset(addr, 0, LARGE_MEMORY_SIZE);

    printf("Press Enter to continue\n");
    fgets(str,2,stdin);

    // Clean up
    if (munmap(addr, LARGE_MEMORY_SIZE) == -1) {
        perror("munmap");
        return 1;
    }

    return 0;
}

Se si esegue il programma, non è direttamente osservabile se THP viene usato dal programma.

È possibile verificare l'utilizzo THP complessivo nel sistema esaminando il /proc/meminfo file. Controllare il AnonHugePages campo per determinare la quantità di memoria che usa THP. Questo file fornisce solo statistiche a livello di sistema.

Per sapere se un processo usa THP, è necessario esaminare il smaps file nella /proc directory del processo in questione. Ad esempio, in /proc/2275/smapscercare una riga contenente la parola heap (mostrata qui all'estrema destra).

Screenshot dell'utilizzo THP dal programma C di esempio.

Questo esempio mostra che un segmento di memoria di grandi dimensioni è stato allocato e contrassegnato come THPeligible(THP sono in uso). Usando madvice syscall, l'allocazione di questo blocco di memoria è molto più efficiente. È possibile ottenere la stessa efficienza usando Huge Pages. A seconda delle dimensioni dell'allocazione, il kernel potrebbe assegnare pagine standard di 4 KB o blocchi contigui più grandi. Questa ottimizzazione può migliorare le prestazioni per le applicazioni a elevato utilizzo di memoria.

Per altre informazioni, vedere Transparent Hugepage Support.

NUMA

Se le applicazioni sono in esecuzione in un sistema NUMA con più nodi, è importante conoscere la capacità di memoria di ogni nodo. Tutti i nodi possono accedere alla memoria totale del sistema. Tuttavia, si ottengono prestazioni ottimali se i processi eseguiti in un particolare nodo NUMA operano sulla memoria sotto controllo diretto di tale nodo. Se il nodo locale non può soddisfare una richiesta di memoria, il sistema alloca memoria da un altro nodo. Tuttavia, l'accesso alla memoria tra i nodi introduce la latenza e può causare penalità per le prestazioni. È pertanto necessario monitorare la località della memoria per assicurarsi che i carichi di lavoro siano allineati alle risorse di memoria dei nodi NUMA assegnati.

Lo screenshot seguente mostra un esempio della configurazione NUMA del sistema.

Screenshot dell'output numactl.

Questa configurazione mostra che l'accesso alla memoria all'interno dello stesso nodo ha un livello di distanza pari a 10. Se si vuole accedere alla memoria Node 1 da Node 0, questo processo ha un valore di distanza elevato pari a 12 ma è comunque gestibile. Tuttavia, se si vuole accedere alla memoria da NODE 3NODE 0, il livello di distanza diventa 32. Questo processo è ancora fattibile, ma è anche tre volte più lento. È utile considerare queste differenze quando si diagnosticano problemi di prestazioni o si ottimizzano i carichi di lavoro associati alla memoria. Per altre informazioni, vedere questo articolo sul kernel Linux. Per una descrizione dello numactl strumento, vedere numactl(8).

Per determinare se esiste un riallineamento dei processi e sarebbe necessario un nodo diverso, usare lo numastat strumento . La documentazione per questo strumento è disponibile in numastat(8). Lo migratepages strumento migratepages(8) consente di spostare le pagine di memoria nel nodo corretto.

Overcommitment e killer OOM

L'overcommitment è una scelta di progettazione importante che può influire seriamente sulle prestazioni e sulla stabilità del sistema. Il kernel Linux supporta tre modalità:

  • 'Euristico'
  • 'Impegnati sempre oltre il possibile'
  • Non prendere troppi impegni

Per impostazione predefinita, il sistema usa lo Heuristic schema . Questa modalità offre un compromesso bilanciato tra consentire sempre l'overcommit della memoria e negarlo rigorosamente. Per altre informazioni, vedere la documentazione del kernel.

Un'impostazione overcommitment non corretta potrebbe impedire alle pagine di memoria di allocare memoria. Potenzialmente, questa condizione potrebbe ostacolare la creazione di nuovi processi o impedire alle strutture del kernel interno di acquisire memoria sufficiente.

Se si verifica che il problema sia correlato all'allocazione di memoria, la causa più probabile di questo problema è che non ci sono risorse sufficienti per il kernel. In questo tipo di situazione, l'assassino dell'OOM (Out-Of-Memory) potrebbe essere richiamato. Il suo processo consiste nel liberare alcune pagine di memoria da usare dalle attività del kernel o da altre applicazioni. Richiamando l'assassino OOM, il sistema avvisa che ha raggiunto i limiti delle risorse. Se è possibile eliminare la possibilità di una perdita di memoria, la causa di questa condizione potrebbe essere che troppi processi sono in esecuzione o che i processi che consumano molta memoria. Per risolvere il problema, prendere in considerazione l'aumento delle dimensioni della macchina virtuale o lo spostamento di alcune applicazioni in un altro server.

Log di sistema generati durante gli eventi OOM

Questa sezione presenta una tecnica per identificare il momento in cui viene attivato OOM Killer e per scoprire quali informazioni vengono registrate dal sistema.

Il semplice programma C seguente verifica la quantità di memoria che può essere allocata dinamicamente in un sistema prima che non riesca.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ONEGB (1 << 30)
int main() {
        int count = 0;
        while (1) {
                int *p = malloc(ONEGB);
              if (p == NULL) {
                printf("malloc refused after %d GB\n", count);
                return 0;
               }
        memset(p,1,ONEGB);
        printf("got %d GB\n", ++count);
     }
}

Questo programma indica che l'allocazione di memoria ha esito negativo dopo circa 3 GB.

Screenshot dell'errore di allocazione della memoria.

Quando il sistema esaurisce la memoria, viene richiamato l'assassino OOM. È possibile visualizzare i log correlati usando il dmesg comando . Le voci di log iniziano in genere come illustrato nello screenshot seguente.

Screenshot del log dell'OOM killer richiamato.

In genere, le voci si concludono con un riepilogo dello stato della memoria.

Screenshot dello stato di memoria insufficiente.

Tra queste voci è possibile trovare informazioni dettagliate sull'utilizzo della memoria e sul processo selezionato per la terminazione.

Screenshot del dettaglio completo di OOM n. 1. Screenshot del dettaglio completo di OOM n. 2.

Da queste informazioni è possibile estrarre le informazioni dettagliate seguenti:

4194160 kBytes physical memory 
No swap space
3829648 kBytes are in use

Nell'esempio di log seguente, il processo malloc ha richiesto una singola pagina di 4 KB (order=0). Anche se la pagina di 4 KB è piccola, il sistema era già sotto pressione. Il log mostra che la memoria è stata allocata dalla "Zona normale".

Screenshot dell'esempio di registro relativo a malloc.

La memoria disponibile (free) è di 29.500 KB. Tuttavia, la filigrana minima (min) è 34.628 KB. Poiché il sistema è al di sotto di questa soglia, solo il kernel può usare la memoria rimanente e le applicazioni spazio utente vengono negate. L'assassino OOM viene richiamato a questo punto. Seleziona il processo con l'utilizzo più elevato oom_score e di memoria ('RSS'). In questo esempio il processo malloc aveva un oom_score valore pari a 0, ma ha anche il valore più alto RSS (917760). Viene quindi selezionato come obiettivo per l'eliminazione.

Monitorare la crescita graduale della memoria

Gli eventi OOM sono facili da rilevare perché i messaggi correlati vengono registrati nella console e nei log di sistema. Tuttavia, gli aumenti graduali dell'utilizzo della memoria che non causano un evento OOM possono essere più difficili da rilevare.

Per monitorare l'utilizzo della memoria nel tempo, usare lo sar strumento del sysstat pacchetto. Per concentrarsi sui dettagli della memoria, usare l'opzione "r", ad esempio "sar -r".

Output di esempio

Screenshot delle informazioni sulla memoria di sar.

In questo caso, l'utilizzo della memoria aumenta per circa due ore. Poi, torna al quattro per cento. Questo comportamento potrebbe essere previsto, ad esempio durante le ore di accesso di punta o le attività di creazione di report a elevato utilizzo di risorse. Per determinare se questo comportamento è normale, potrebbe essere necessario monitorare l'utilizzo in diversi giorni e quindi correlarlo con l'attività dell'applicazione. L'utilizzo elevato della memoria non è necessariamente un problema. Dipende dal carico di lavoro e dal modo in cui le applicazioni sono progettate per l'uso della memoria.

Per individuare i processi che utilizzano la maggior parte della memoria, usare pidstat.

Output di esempio

Screenshot dell'output di pidstat.

Questo output visualizza tutti i processi in esecuzione e le relative statistiche. Un altro approccio consiste nell'usare lo strumento "ps" per ottenere risultati simili: ps aux --sort=-rss | head -n 10

Output di esempio

Screenshot dell'output ps aux.

Perché ordinare per RSS?

Resident Set Size (RSS) è la parte della memoria di processo contenuta nella RAM (memoria fisica non scambiata). Al contrario, la dimensione del set virtuale (VSZ) rappresenta la quantità totale di memoria riservata dal processo, inclusa la memoria non sottoposta a commit. Committed memory fa riferimento a pagine effettivamente scritte nella memoria fisica. Se si sta provando a identificare i processi che utilizzano la maggior quantità di memoria fisica (inclusa la memoria di swap), concentrarsi sulla colonna RSS. Nell'output di esempio il snapd processo sembra usare molta memoria, ma il valore RSS è basso. Il malloc processo ha valori simili VSZ e RSS che indicano che usa attivamente più di 1,3 GB di memoria.

Clausola di esclusione di responsabilità per il contatto di terze parti

Microsoft fornisce informazioni di contatto di terze parti per aiutarti a trovare ulteriori informazioni su questo argomento. Queste informazioni di contatto sono soggette a modifica senza preavviso. Microsoft non garantisce l'accuratezza delle informazioni di contatto di terze parti.

Contattaci per ricevere assistenza

Per domande o richieste di assistenza, creare una richiesta di supporto o chiedere supporto alla community di Azure. È anche possibile inviare commenti e suggerimenti sul prodotto alla community di commenti e suggerimenti di Azure.