2009-06-06 8 views
14

Sto sviluppando un'applicazione web con Merb e sto cercando una libreria di elaborazione delle immagini sicura e stabile. Lavoravo con Imagick in PHP, poi mi sono trasferito in ruby ​​e ho iniziato a usare RMagick. Ma c'è un problema. Script a esecuzione prolungata che causano perdite di memoria. Esistono due soluzioni, ma non so quale sia la più stabile. Allora, cosa ne pensate?Come gestire le perdite di memoria in RMagick in Ruby?

In questo momento, la mia app utilizza l'API interna che ho scritto per elaborare le immagini, in PHP. Funziona su server separati insieme ad altre applicazioni, quindi non è un grosso problema. Ma penso che non sia una buona architettura.

In ogni caso, considererò qualche suggerimento pratico.

risposta

11

Anche io ho riscontrato questo problema - la soluzione è forzare la garbage collection.

Dopo aver riassegnato la variabile immagine a una nuova immagine, utilizzare semplicemente GC.start per assicurarsi che il vecchio riferimento venga rilasciato dalla memoria.

Nelle versioni successive di RMagick, credo anche che puoi anche chiamare distruggere! sull'immagine al termine dell'elaborazione.

Una combinazione dei due probabilmente garantirebbe di essere coperto, ma non sono sicuro dell'impatto sulla performance reale della vita (suppongo che sia trascurabile nella maggior parte dei casi).

In alternativa, è possibile utilizzare mini-magick che è un wrapper per il client della riga di comando di ImageMagick.

+1

sì, era una delle soluzioni di cui ho sentito parlare. ma dall'altra parte, chiamare gc tutto il tempo non è una buona idea (ho visto un articolo a riguardo qualche tempo fa). può causare rallentamenti. e inoltre, gc è un'operazione molto "costosa". Non sono sicuro, ma al momento non ho opzioni. anche, c'è una versione migliorata di rmagick, ma ancora, ha perdite di memoria, solo la questione del tempo –

+0

Il mio consiglio sarebbe di profilare la garbage collection e vedere se è possibile gestirlo. Un'alternativa sarebbe ImageScience (http://seattlerb.rubyforge.org/ImageScience.html) ma non è così efficace come RMagick. – Matt

+4

Chiamando Image # destroy! nelle immagini intermedie, sono riuscito a ridurre l'utilizzo della memoria di un ordine di grandezza nella mia app Rails rilegata con RMagick, da 200 MB a> 40 MB. Il trucco consisteva nel mantenere il numero di oggetti Magick :: Image/Magick :: ImageList nella RAM allo stesso tempo il più basso possibile. Chiamare GC.start non era necessario. – foz

-1

Questo non è dovuto a ImageMagick; è dovuto a Ruby stesso, ed è un problema ben noto. Il mio suggerimento è di dividere il programma in due parti: una parte di lunga durata che alloca poca memoria e si occupa solo del controllo del sistema, e un programma separato che funziona effettivamente l'elaborazione. Il processo di controllo di lunga durata dovrebbe fare abbastanza per trovare un po 'di lavoro per un processo figlio che genera e il bambino dovrebbe eseguire tutta l'elaborazione per quel particolare oggetto di lavoro.

Un'altra opzione sarebbe quella di lasciare i due combinati, ma dopo che un'unità di lavoro è completa, utilizzare exec per sostituire il processo con una versione appena avviata dello stesso programma, che dovrebbe cercare un altro oggetto di lavoro, elaborarlo e esegui di nuovo se stesso.

Ciò presuppone che gli elementi di lavoro siano piuttosto grandi, che sono quasi certamente se si utilizza ImageMagick. Se non lo sono, scoprirai che il sovraccarico di generazione di un nuovo processo e l'interpretazione dell'interpretazione di Ruby dell'intero programma inizia a diventare un po 'troppo grande. È possibile gestire questo problema facendo in modo che il programma esegua più unità di lavoro (ad esempio, dieci o cento) prima di eseguire nuovamente la propria esecuzione.

+0

come ho detto nel post originale, attualmente sto usando un pezzo di programma esterno implementato come API. –

+1

Sì, ora vedo. Immagino che il mio consiglio sia ancora lo stesso, tranne che potresti interpretarlo come, "seguilo" ... –

5

In realtà, non è proprio un problema specifico di Ruby, anche altri Interpreti lo condividono. Il problema concreto è che il GC di Ruby vede solo la memoria allocata da Ruby stessa, e non dalle librerie esterne (con la notevole eccezione della libreria che utilizza le funzionalità di gestione della memoria di Rubys). Quindi, un oggetto ImageMagick in Ruby è molto piccolo, ma l'immagine nello spazio gestito da ImageMagick è grande. Quindi, questa non è una perdita di per sé, ma si comporta come tale. Rubys Garbage Collector non entra mai in azione se il tuo Processo rimane sotto un certo limite (8 MB standard). Dato che ImageMagick non crea mai oggetti di grandi dimensioni nello spazio Ruby, probabilmente non esegue mai il kicking. Quindi, o usi il metodo proposto per generare un nuovo processo o usare exec. Un altro piuttosto elegante è quello di avere un servizio di elaborazione delle immagini nel back-end che si biforca per ogni attività. Un altro sarebbe avere un qualche tipo di monitoraggio sul posto che avvia il GC ogni tanto.

C'è un'altra libreria denominata MagickWand di Timothy Paul Hunter (l'autore di RMagick) che tenta di risolvere questi problemi e creare un'API migliore. È in alpha e richiede una versione piuttosto nuova di ImageMagick, però.

+0

Beh, basandomi sul commento alla mia risposta, potrei sbagliarmi sui dettagli di questa situazione. Ma sono abbastanza sicuro nel dire che se c'è una perdita, è molto più probabile in Ruby che in una libreria ben utilizzata come ImageMagick. (Bene, ok, c'è anche la possibilità che sia correlato all'interfaccia tra i due, ma che conta come Ruby per me.) Ma penso che la tua caratterizzazione generale del comportamento del GC sia errata. Finché il codice della colla che si interfaccia alla libreria sta gestendo la memoria in modo appropriato, qualunque cosa la libreria assegni dovrebbe essere vista dal GC. –

+1

Come ho detto: non è una perdita per definizione, sebbene si comporti come uno. Se il GC elimina l'oggetto, la memoria viene rilasciata correttamente. Il punto è che il GC non vede mai abbastanza memoria allocata per dare il calcio d'inizio. E Rubys GC è davvero conservatore quando entra in gioco, in particolare il fatto che non entra in gioco finché la dimensione della memoria vista è inferiore a una certa dimensione. Hai ragione: una libreria ben realizzata che garantisce che tutta la memoria allocata possa essere vista da Ruby risolve questo problema - questo è fondamentalmente ciò che sta facendo MagickWand. – Skade

6

Quando si utilizza RMagick è importante ricordare di distruggere l'immagine una volta terminato, altrimenti si riempirà la directory/tmp quando si lavora con grandi serie di immagini. Ad esempio, devi chiamare destroy!

require 'RMagick' 

Dir.foreach('/home/tiffs/') do |file| 
    next if file == '.' or file == '..' 
     image = Magick::Image.read(file).first 
     image.format = "PNG" 
     image.write("/home/png/#{File.basename(file, '.*')}.png") 
     image.destroy! 
end