Problemi gravi di Memory Leak con QML/Javascript
-
Ciao a tutti
Ho dei problemi piuttosto gravi di Memory Leak con il mio progetto, e se non li risolvo sarò messo veramente male, quindi mi rivolgo a voi come ultima spiaggia perchè la situazione è davvero critica.
Ho fatto la grafica in QML e la logica in Javascript, per semplicità perchè già lo conoscevo, e anche perchè ho migrato dalla vecchia piattaforma che era una webapp, e l'ho convertita in QT per motivi legati sopratutto alle performance, che sono molto migliori.
Commentando tutte le funzioni usate in Javascript non ho un aumento della memoria, e quindi sto andando a decommentare un pezzo alla volta per scovare il problema, e già alla prima che decommento ho problemi, e si tratta di una banale funzione che prende la data dal sistema operativo.
Per prendere la data del sistema operativo uso una chiamata HTTP con XMLHttpRequest e questo genera un aumento della memoria.
Semplificando per scovare l'errore noto che ho un aumento della memoria anche togliendo di mezzo XMLHttpRequest, e solamente mettendo il valore dell'ora e della data sul Text di QML, con un certo ID, quindi facendo hour.text = ORA, e date.text = DATA. Anche valorizzando la stringa come property ho lo stesso risultato: aumento della memoria quando valorizzo questa variabile che poi viene impsotata come text nella grafica.
Metto alcune parti di codice per scoprire il problema intanto di queste banali parti che non funzionao.
Text { id: hour color: (nightMode) ? "white" : "#387abe" font.pixelSize: 16 font.bold: true Layout.leftMargin: 15 } Text { id: date color: (nightMode) ? "white" : "#387abe" font.pixelSize: 16 font.bold: true } Timer { id: dateTimeTimer interval: 1000 repeat: true running: true triggeredOnStart: true onTriggered: { Main.getDate(); } }
E infine la parte javascript:
function getDate() { var targetDate; var timestamp; var localdate; request('http://' + ipAddress + '/api/api.php?cmd=getDate', null, "GET", 2000, function (data_json) { if (data_json.status === 200) { var jsonData = JSON.parse(data_json.responseText); dateLocal = jsonData.date; hourLocal = jsonData.hour; } else { targetDate = new Date(); timestamp = targetDate.getTime()/1000; localdate = new Date(timestamp * 1000) hourLocal = localdate.toLocaleString(Qt.locale("it_IT"), "hh:mm"); dateLocal = localdate.toLocaleString(Qt.locale("it_IT"), "dd/MM/yyyy"); } }); } function request(url, body, method, timeout, callback) { var xhr = new XMLHttpRequest(); xhr.onreadystatechange = (function(myxhr) { return function() { if(myxhr.readyState === 4) { callback(myxhr); } } })(xhr); xhr.open(method, url); if (body) xhr.send(body); else { xhr.send(); } }
Vorrei capire in delle funzioni così banali cosa sbaglio, da far aumentare la memoria senza che essa venga liberata.
Aggiungo che per leggere solo la data io mi aspetto che almeno qui la memoria rimanga sempre la stessa e che non aumenti di volta in volta solamente per leggere la data, perchè dato che deve girare in un sistema embedded che ha 1 GB di memoria, e dovrebbe poter resistere acceso anche per un mese consecutivo, è di vitale importanza non aver problemi di questo tipo. Infatti con i problemi di memoria riscontrati l'applicativo va in crash dopo neanche una settimana a causa appunto dell'esaurimento della memoria.
Se può aiutare come informazione gira su sistema operativo Yocto.
Saluti
Marco -
Salve, buongiorno.
Dalle prove fatte sembra che anche se lo eseguo nel mio PC ho dei minimi aumenti di memoria, che diventano sempre più grandi a seconda delle funzioni che attivo per fare debug.
In particolare ho notato che prendendo solo la parte dell'else ma fuori dalla request funziona fino a che non assegno la data alla variabile hourLocal e dateLocal, che sono due variabili assegnate come property string nel main.qml principale, che poi assegno al text dela data e l'ora sulla parte grafica.
Nel codice ho dimenticato di inserire questa parte, ma nei 2 text ci sono appunto un text: hourLocal e text: dateLocal.
Grazie
Saluti
-
Vorrei dire che se lasci il programma in esecuzione per ore sul tuo pc non avrai significativi aumenti di memoria occupata mentre nel dispositivo embedded sì.
In passato mi è successa una cosa similare.
Il problema era dipeso dal fatto che il dispotitivo non supportava bene opengl etc. -
Il problema che solo per prendere l'ora da una richiesta http per prenderla dalla memoria e aggiornarla sulla grafica, io mi aspetterei non di non avere significativi aumenti, ma di non averli affatto, o mi sbaglio? È normale che anche se poco aumenta la memoria per una funzione così stupida, come leggere l'ora di sistema e aggiornarla nella grafica ogni secondo?
Perchè come ripeto, anche se aumentasse poi dovrebbe anche liberarsi, perchè a lungo andare anche di poco potrebbe poi diventare un problema. Ora provo ad eseguirlo anche nel pc lo stesso codice che sta girando ora e vediamo.
Saluti,
Marco -
Per chiarire,
solitamente qml é compilato JIT (se non erro, di default).
Quindi gli oggetti non sono "preallocati" in quanto tale, ma per ogni singola richiesta (request) uscente, il codice in quanto tale
é rieseguito; di conseguenza si noterá un certo consumo "dinamico" di memora.
Qt solitamente si "mangia" parecchia memoria specialmente nel boot dell eseguibile (dato il caricamento di tutte le dipendenze);
in piu, lo Scene graph non é "leggerissimo", e in cima poi chiaramente si trovano gli asset (foto etc).Valgrind solitamente é abbastanza specifico nell identificare problemi di leak.
https://doc.qt.io/qtcreator/creator-valgrind-overview.html
Non so se é gia stato provato?
-
@m2dtkast Ho fatto questa prova e sembrava che se al posto della data assegnavo una stringa statica la memoria non aumentava. Avevo fatto questa prova, ma il mio programma è fatto in larga parte da dati dinamici, quindi magari la maggior parte dei problemi dipende da questo, e se trovo il problema sulla data magari riesco a risovlerlo in gran parte del codice.
Saluti,
Marco -
@marco_88 said in Problemi gravi di Memory Leak con QML/Javascript:
else { targetDate = new Date(); timestamp = targetDate.getTime()/1000; localdate = new Date(timestamp * 1000)
Suggerirei di deallocare esplicitamente gli oggetti allocati sull heap nel JS (via i vari new) dopo l uso. Tipo:
targetData = undefined
localdate = undefinedper rimuovere le referenze, considerando che gli oggetti servono "unicamente" per generare la controparte leggibile tramite
le toLocaleString questo non dovrebbe distubare -
@m2dtkast Purtroppo mettendo tutte le variabili javascript su undefined dopo l'utilizzo continua a non funzionare. Se invece faccio hourLocal e dateLocal = "" poi non leggo più la data nell'interfaccia, quindi non riesco a capire.
Saluti,
Marco -
Con QWidget, quindi senza qml, corretto? Qualche esempio posso trovarlo e testarlo.
Per quanto riguarda il sistema operativo è aggiornato per quanto riguarda quello che c'è per questo device.
Il sistema operativo è yocto, e credo sia aggiornato: https://variwiki.com/index.php?title=Yocto_Build_Release&release=RELEASE_THUD_V1.0_VAR-SOM-MX6
Kernel è la versione 4.14.78 e il device come mostrato dal link è un variscite con chip iMX6.
In effetti testandolo più a lungo sul PC Desktop che è Ubuntu 18.04 con kernel 5.4.0-58 e il test l'ho fatto con la stessa versione di QT che gira sul device, e qui ho avuto adirittura dei piccoli decrementi, seguiti da piccoli incrementi, che è quello che mi aspetterei, e infatti a me sta bene che ogni tanto ci siano degli incrementi, se poi sono seguiti dai decrementi.
Aprendo top sul desktop quando ho aperto il programma su RES segnava 81848 e ora segna 81840, anche con dei minimi su 81797, invece aprendo top sul device quando ho aperto aveva 51548 e ora è 52188, stessa identica versione del programma, stessa versione di QT, ma ovviamente sistema diverso.
Saluti,
Marco -
@mrdebug Ciao, ho fatto una scoperta riguardo questo problema. In pratica mi sono concentrato oltre che sulla memoria utilizzata dal mio applicativo, anche dalla memoria libera totale del sistema. Infatti le volte che mi andava in crash l'applicativo QT succedeva che nel giro di 3-4 giorni si esauriva tutta la memoria (la board ha 1 GB di memoria).
Allo start ci sono circa 743 MB liberi compreso l'applicativo scritto in QT, e in effetti controllando bene l'aumento di memoria dell'applicativo mio non è così elevato da esaurire più di 700 MB in pochi giorni.
Ora ho riattivato tutte le funzioni javascript dell'applicativo e riportato allo stato originale e ho notato che la memoria è passata da 54.6 MB a 55.6 MB dopo aver cliccato su un pulsante nell'interfaccia, ma dopo di che non ci sono stati altri aumenti di memoria, è rimasta stabile su 55.6 MB per 1 ora, ma la memoria libera è scesa a dismisura. Mentre l'applicativo in un ora ha avuto solo quell'aumento di 1 MB, il sistema ha perso in totale circa 20 MB, e se in un ora la memoria da 743.6 MB è scesa a 723.6 MB puoi capire che il sistema dura davvero poco prima che chiuderà l'applicativo per mancanza di memoria.
La memoria oscillava ma sempre a ribasso, scendeva sempre di più all'infinito. Ho provato adesso ha disabilitare il servizio che fa partire l'applicativo QT e non ci sono più grandi oscillazioni e la memoria è stabile, da 773 MB a 772.8.
Questo mi porta a pensare che l'applicativo di per se non causa aumento di memoria, ma causa aumenti di memoria indirettamente. Problemi di driver nel device che fanno uso della grafica? Php che viene richiamato per le richieste? Python che viene richiamto per degli script? L'applicativo usa EGLFS per partire e Yocto è in modalità Framebuffer. Può essere lì il problema? Non ho proprio idea e sto uscendo scemo per capire le cause. Ora farò delle analisi più approfondite anche sul PC, perchè apparentemente l'applicativo non cresceva particolarmente, però non ho verificato la memoria totale libera, anche se su PC ci sono tante cose e potrebbe essere complciato fare questo tipo di analisi.
Aggiungo inoltre che la memoria che cresce sul device quando l'applicativo gira, non è oscillante, ma cresce solamente, ogni tanto ha dei piccoli decrementi, ma gli incrementi sono molti di più, e comunque tutto si stabilizza quando l'applicativo mio non gira.
Saluti,
Marco -
Ho avuto lo stesso problema anni fa. L'applicativo si appoggiava a xorg. Ne è emerso che l'opengl non era ben supportato per cui era xorg a mangiarsi tutta la memoria.
Non ho potuto fare altro che utilizzare qwidgest e non qml, di modo da non dover usare accelerazioni grafiche particolari.
Il problema accade con qualsiasi programma che faccia uso di grafica in modo spinto, non solo con Qt.
Hai modo di provare ad usare wayland e vedere se il problema si risolve?
La versione di yocto non mi sembra recente, stai usando la versione ufficiale rilasciata da chi ti fa la scheda o l'avete fatta voi?
Il progetto cosa fa? -
@mrdebug Ciao. La versione di Yocto non è la più recente perchè per la scheda DART-MX6 di Variscite, sembra esser la versione più recente disponibile. Anche io volevo una versione più recente, però questa è la più recente che ho trovato per questa scheda. Ora i test li sto facendo sull'Evaluation Kit ufficiale di Variscite, però il progetto è una scheda fatta da un azienda, ma usano questa scheda di Variscite comunque. È un progetto abbastanza articolato, deve essere performante il più possibile e non deve avere rallentamenti, perchè è per uso medico. Legge i dati da canbus, manda gli allarmi, ci sono dei riscaldatori, sensore di ossigeno, spo2, etc.
Sull'Evaluation kit qualche test posso farlo, però per usare wayland cosa dovrei fare? Per quanto riguarda qwidget comporterebbe però debellare qml, e ho già scritto tutto il codice in qml, e sarebbe un bel casino, visto anche che per concludere questo primo step non avrei poi tanto tempo a disposizione. Però a livello grafico non è che deve fare niente di particolarmente impegnativo. La parte più impegnativa forse sono i grafici, perchè ha anche dei grafici, ma in questi test neanche ho aperto la pagina dei grafici, e fra l'altro non ci sta attaccato niente, e quindi i dati neanche li legge perchè nell'evaluation kit solo si apre la grafica, ma sostanzialmente non fa nulla perchè non è la macchina originale che invece risiede in ufficio.
Fammi sapere se ci sta un modo rapido e veloce di risolvere questo problema perchè non sai da quanto tempo è che sto combattendo con questo.
Saluti,
Marco