Strategie per l'implementazione del rilevamento perdite di memoria [chiuso]

7

Quale strategia useresti per rilevare perdite di memoria?

Mi è stata fatta questa domanda in un'intervista concentrata principalmente su C ++ e non ho trovato risposta. Ho detto che ci sono strumenti che lo fanno e che li userei, ma non era soddisfacente, e non avevo nient'altro che potessi pensare.

Quando ho cercato in seguito una risposta su Internet, tutto quello che ho trovato erano riferimenti su come utilizzare diversi strumenti e non su quali strategie utilizzare.

    
posta user65252 17.03.2017 - 03:07
fonte

8 risposte

4

Potresti averle fatto una domanda di chiarimento - "quale tipo di perdita di memoria?" :)

Ci sono diversi tipi di perdite di memoria - ovviamente, alcune sono banali da osservare e rilevare, altre sono su un altro estremo. Dai un'occhiata a questo post per i tipi di perdite.

Ci sono alcuni strumenti per rilevare le perdite. Molti di questi sono strumenti di analisi dinamica come DevPartner Boundschecker o Valgrind. Alcuni strumenti di analisi statica possono anche rilevare alcuni (banali) casi di perdite di memoria.

Se devi scrivere un rilevatore di perdite di memoria, devi tenere traccia di tutte le allocazioni di memoria e deallocazioni. Questo può essere fatto in molti modi: agganciare direttamente le chiamate API di sistema o strumentare il codice (codice sorgente, codice intermedio o anche codice eseguibile della macchina) per tenere d'occhio le allocazioni / deallocazioni.

    
risposta data 17.03.2017 - 13:10
fonte
3

Personalmente la mia risposta sarebbe:

Per prima cosa utilizza gli avvisi che il compilatore fornisce, che nel caso di alcune aziende ben conosciute, le impostazioni predefinite della catena di strumenti sono silenziose, assicurandosi che tutti gli avvisi siano stati abilitati. In realtà raccomanderei di attivare tutti gli avvisi del compilatore e di utilizzare uno standard di codifica che richiedesse avvisi zero.

Vorrei anche prendere in considerazione l'esecuzione dell'analisi statica con uno strumento come Coverity, o anche PCLint, dato che sono ottimi per individuare potenziali problemi come questo.

Quindi eseguirò l'intera suite di test con uno strumento di profilazione per garantire che avessimo il più possibile una copertura del 100%, quindi di nuovo sotto un controllo come Valgrind, o anche il debugger se supporta questo.

Infine, e probabilmente solo se la catena di strumenti & target non supportava tali strumenti e se non potessi comprare in una libreria per fare questo , prenderei in considerazione l'implementazione del mio, sia in un build speciale per test o come componente di sfondo del prodotto.

Solo allora, se gli avessi chiesto di espandermi, inizierei a implementarlo.

Se viene chiesto perché questa strategia risponderò costo , gli autori del compilatore hanno trascorso molte migliaia di ore lavorative affrontando problemi come perdite di memoria, così come gli sviluppatori di strumenti di analisi statica e entrambi forniscono 100 % di copertura del codice. Strumenti come Valgrind possono svolgere un lavoro eccellente, ma solo se il codice viene esercitato al 100% mentre stanno guardando, quindi utilizzando uno strumento di profilazione per stabilire la copertura.

Generalmente gli strumenti di profilazione della memoria non appartengono al codice di produzione dato che, per la maggior parte, normalmente si tratta di un overhead piuttosto pesante e di solito è più efficace abilitare, installare o abilitare la garbage collection.

Potrebbero aver cercato di menzionare RAII, Acquisizione risorse è inizializzazione , ma rigorosamente quello è una strategia per evitare piuttosto che rilevare perdite di memoria.

    
risposta data 17.03.2017 - 07:36
fonte
2

Esistono due strategie principali:

Una perdita è un malloc senza free (che in C ++ è un new senza delete ). I malloc globali senza uno sono economici, in quanto generalmente non è necessario. La pulizia del processo alla fine del ciclo di vita del programma rende sempre più veloce la ricerca degli alberi nel puntatore e la aggiunge all'elenco libero. Ma i mallocs nelle funzioni annidate possono essere chiamati abbastanza spesso, e questa memoria mangia, ha bisogno di un abbinamento gratuito. Un alloca sullo stack non deve essere liberato del tutto. Questa è la bellezza della pila.

L'aggiunta di malloc / free hook da sola non funziona per rilevare perdite. È necessario interrogare le statistiche di hook raccolte alla fine. Utilizzando strumenti di compilazione, come i correttori di limiti non funzionano per il rilevamento di perdite. È necessario contrassegnare e contare ogni puntatore del malloc e osservare una corrispondenza libera in fase di esecuzione.

  1. Interpretazione / jit delle CPU in una sandbox, rilevamento di malloc e chiamate gratuite in fase di runtime. Vedi come funziona valgrind . valgrind memcheck funziona con ogni binario ed è la soluzione generale.
  2. Aggiungi i malloc / hook gratuiti al momento della compilazione come nel nuovo AddressSanitizerLeakSanitizer , come in -fsanitize=leak o -fsanitize=address e ASAN_OPTIONS: detect_leaks=1 . Questo è ovviamente molto più veloce, ma ha bisogno di esp. binari preparati.

Nei linguaggi dinamici è molto più semplice: puoi agganciare facilmente le chiamate di allocazione della memoria VM e segnalare le chiamate gratuite mancanti alla fine del programma.

    
risposta data 18.03.2017 - 08:09
fonte
0

Penso che tu fossi sulla buona strada. La semplice risposta è "monitoraggio" e questo è un buon punto di partenza per la conversazione. Avrei detto qualcosa del tipo:

I would implement a monitoring tool that would report the memory allocation of all programs on the machine. Then I would implement an alerting system that would send the on call person a text / phone call depending on the severity of the alert.

Potrebbero aver cercato di menzionare uno strumento specifico o possibilmente come funziona lo strumento?

Non ti sarebbe capitato di candidarti per una posizione DevOps, vero? :)

In ogni caso, torna alla domanda di perdita di memoria. Il modo più semplice per farlo sarebbe implementare uno strumento come un profiler .NET, che fondamentalmente ha visibilità nel codice e riporta le metriche su:

  • Perfomrance del metodo (fino al punto in cui è possibile visualizzare lo stack di chiamate e le prestazioni per tutti i metodi fino in fondo)
  • Consumo della CPU
  • Consumo di memoria

Ho usato New Relic in passato per questo (il loro prodotto APM), che è uno strumento fantastico.

E, sì, probabilmente c'è un leggero effetto "parassitario" che puoi ottenere usando questo tipo di strumento, ma i vantaggi superano di gran lunga quello, a mio parere.

Inoltre, se ti chiedessero come scriveresti il tuo strumento, ti spiegherei innanzitutto che non ne vale la pena, perché reinventare la ruota? Ma se loro veramente dovessero sapere, allora direi di creare un agente che verrebbe installato sulla macchina per monitorare tutte le app in esecuzione e riportare il loro utilizzo su qualche storage / API che potrebbe essere riportato più tardi. Ma poi dovresti anche costruire lo strumento di segnalazione e legarlo a qualche tipo di strumento di avviso. Tuttavia, esistono strumenti esistenti che valgono il prezzo per questo tipo di cose.

Alla fine avrei fatto un doppio down sull'approccio agli strumenti. Se a loro non piacesse, forse avrebbero preferito scriverlo da soli? Ma onestamente questo non vale la pena e probabilmente spenderebbero di più nello sviluppo / mantenimento di qualcosa di simile piuttosto che comprare uno strumento. E se sono così testardi di quanto probabilmente hai schivato un proiettile comunque.

    
risposta data 17.03.2017 - 04:22
fonte
0

Lasciando da parte i vari strumenti, gli avvertimenti ambientali e il passaggio a strutture di memoria statiche, posso solo immaginare che cosa stessero cercando qui è una sorta di meccanismo di conteggio dei riferimenti in cui un contatore di qualche tipo viene incrementato quando la memoria viene allocata e decrementata quando la memoria è deallocata.

Questo è un metodo portatile che funziona su qualsiasi piattaforma e non richiede ulteriori strumenti.

    
risposta data 17.03.2017 - 15:02
fonte
0

Una strategia per il rilevamento delle perdite di memoria è il test delle prestazioni. Il test delle prestazioni non è solo per trovare i limiti dell'applicazione, ma dovrebbe anche includere un carico statico su un intervallo di N ore. Ad esempio, eseguire l'applicazione sotto carico per 4 ore.

Se lo si fa come parte del processo di sviluppo, le perdite di memoria possono essere identificate prima del rilascio e corrette.

Una volta identificata una perdita, è possibile utilizzare uno strumento come WINDBG per trovare i metodi offensivi in modo da poter applicare le correzioni.

    
risposta data 17.03.2017 - 15:24
fonte
0

Ci sono diversi modi per rilevare una perdita di memoria:

  1. Cerca di avere una copertura del codice di test di unità elevata. Quindi eseguire i test unitari, usando valgrind o uno strumento simile. Dal momento che richiede molto tempo, questo può essere fatto come un compito per la costruzione notturna
  2. Chiedi ai tester di controllare l'utilizzo della memoria durante i test, ma questo metodo potrebbe non essere così affidabile, dal momento che potrebbe essere un normale utilizzo della memoria
  3. Automatizza i tuoi test il più possibile. Fai uno stress test guardandoli per lungo tempo. Dopo una settimana, se il tuo PC non ha esaurito la memoria, o diventa molto basso, puoi presumere che non abbia perdite di memoria, o almeno non perdite significative
  4. Esegui la tua applicazione usando valgrind. L'ho messo per ultimo, perché lo uso solo se cerco perdite di memoria, poiché è il metodo più lento e non può essere automatizzato
risposta data 17.03.2017 - 16:03
fonte
0

Suppongo che l'intento fosse di cercare specificamente per le perdite, non solo per le misure igieniche generali. Supporrò anche che l'intento fosse quello di valutare la tua capacità di codifica, quindi non si tratta solo di "usare Valgrind" (o qualche altro strumento), ma dell'approccio generale che avresti impiegato per scrivere lo strumento stesso.

In questo caso, il punto di partenza relativamente semplice sarebbe per l'allocatore di memoria su una traccia di stack ogni volta che viene chiamato e mantenere una traccia di stack associata a ciascun blocco assegnato (insieme ad altre "cose" ovvie come la dimensione dell'allocazione e l'indirizzo del blocco che hai assegnato). Quando un blocco viene liberato, rimuovi la traccia dello stack associata a quel blocco.

Quando il programma si arresta, scarichi tutte le tracce di stack rimaste nella tabella. Poiché hai scaricato lo stack ogni volta, puoi dare una traccia non solo della parte del codice che alloca un blocco che non è stato liberato, ma anche della sequenza completa di chiamate utilizzate per raggiungere quel punto.

Nella maggior parte dei casi, insieme al rilevamento di perdite di memoria, si desidera gestire almeno la scrittura oltre la fine del blocco di memoria. Per fare ciò, tipicamente si vuole allocare un blocco che è sostanzialmente più grande di quanto richiesto, scrivere un modello noto nella memoria extra e dare al client un indirizzo nel mezzo del blocco. Quindi alla fine del programma, se la memoria contiene valori diversi da quello previsto, sai che ha scritto al di fuori del pezzo che ha assegnato.

    
risposta data 19.03.2017 - 09:00
fonte

Leggi altre domande sui tag