La mia domanda riguarda i dati principali e la memoria non viene rilasciata. Sto facendo un processo di sincronizzazione che importa i dati da un WebService che restituisce un json. Carico, in memoria, i dati da importare, eseguire il ciclo e creare NSManagedObjects. I dati importati devono creare oggetti che abbiano relazioni con altri oggetti, in totale ce ne sono circa 11.000. Ma per isolare il problema, sto solo creando gli elementi del primo e del secondo livello, lasciando fuori la relazione, quelli sono 9043 oggetti.Importazione dati principali - Non sblocca memoria
Ho iniziato a controllare la quantità di memoria utilizzata, perché l'app si arrestava in modo anomalo alla fine del processo (con l'intero set di dati). Il primo controllo della memoria è dopo aver caricato in memoria il json, in modo che la misurazione prenda davvero in considerazione solo la creazione e l'inserimento degli oggetti in Core Data. Quello che io uso per controllare la memoria utilizzata è di questo codice (source)
-(void) get_free_memory { struct task_basic_info info; mach_msg_type_number_t size = sizeof(info); kern_return_t kerr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size); if(kerr == KERN_SUCCESS) { NSLog(@"Memory in use (in bytes): %f",(float)(info.resident_size/1024.0)/1024.0); } else { NSLog(@"Error with task_info(): %s", mach_error_string(kerr)); } }
La mia configurazione:
- 1 dell'archivio permanente Coordinatore
- 1 Main ManagedObjectContext (MMC) (NSMainQueueConcurrencyType utilizzata per leggere (solo leggendo) i dati nell'app)
- 1 Sfondo ManagedObjectContext (BMC) (NSPrivateQueueConcurrencyType, undoManager è impostato su nil, utilizzato per importare i dati)
Il BMC è indipendente da MMC, quindi BMC non è un contesto secondario di MMC. E non condividono alcun contesto genitore. Non ho bisogno di BMC per notificare le modifiche a MMC. Quindi BMC ha solo bisogno di creare/aggiornare/cancellare i dati.
Plaform:
- iPad 2 e 3
- iOS, ho provato a impostare la destinazione di distribuzione di 5.1 e 6.1. Non c'è alcuna differenza
- XCode 4.6.2
- ARC
Problema: importazione dei dati, la memoria utilizzata non smette di aumentare e iOS non sembra essere in grado di drenare la memoria anche dopo la fine del processo. Che, nel caso in cui il campione di dati aumenta, porta a Avvisi di memoria e dopo la chiusura dell'app.
di ricerca:
documentazione di Apple
buon riassunto dei punti di avere in mente durante l'importazione dati a Core Data (Stackoverflow)
Test eseguiti e analisi del rilascio di memoria.Sembra avere lo stesso problema di me, e ha inviato una segnalazione di bug di Apple senza alcuna risposta da parte di Apple. (Source)
Importazione e visualizzazione di grandi insiemi di dati (Source)
Indica il modo migliore per importare grandi quantità di dati. Sebbene menzioni:
"Sono in grado di importare milioni di record in un 3MB di memoria stabile senza chiamata -reset."
Questo mi fa pensare che questo potrebbe essere in qualche modo possibile? (Source)
Test:
dati di esempio: la creazione di un totale di 9043 oggetti.
- spento la creazione di relazioni, la documentazione dice che sono "costoso"
- Nessun recupero è stato fatto
Codice:
- (void)processItems {
[self.context performBlock:^{
for (int i=0; i < [self.downloadedRecords count];) {
@autoreleasepool
{
[self get_free_memory]; // prints current memory used
for (NSUInteger j = 0; j < batchSize && i < [self.downloadedRecords count]; j++, i++)
{
NSDictionary *record = [self.downloadedRecords objectAtIndex:i];
Item *item=[self createItem];
objectsCount++;
// fills in the item object with data from the record, no relationship creation is happening
[self updateItem:item WithRecord:record];
// creates the subitems, fills them in with data from record, relationship creation is turned off
[self processSubitemsWithItem:item AndRecord:record];
}
// Context save is done before draining the autoreleasepool, as specified in research 5)
[self.context save:nil];
// Faulting all the created items
for (NSManagedObject *object in [self.context registeredObjects]) {
[self.context refreshObject:object mergeChanges:NO];
}
// Double tap the previous action by reseting the context
[self.context reset];
}
}
}];
[self check_memory];// performs a repeated selector to [self get_free_memory] to view the memory after the sync
}
Measurment:
Passa da 16,97 MB a 30 MB, dopo la sincronizzazione scende a 28 MB. La ripetizione della chiamata get_memory ogni 5 secondi mantiene la memoria a 28 MB.
Altri test senza alcuna fortuna:
- ricreare l'archivio permanente, come indicato nella ricerca 2) non ha effetto
- testato per lasciare che il filo aspettare un po 'per vedere se la memoria restituisce, ad esempio 4
- impostazione contesto a zero dopo l'intero processo
- Esecuzione dell'intero processo senza salvare il contesto in qualsiasi punto (perdendo così le informazioni). Ciò ha dato come risultato il mantenimento di una minore quantità di memoria, lasciandola a 20 MB. Ma continua a non diminuire e ... Ho bisogno delle informazioni memorizzate :)
Forse mi manca qualcosa ma ho davvero provato molto, e dopo aver seguito le linee guida mi sarei aspettato di vedere la memoria diminuire ancora. Ho eseguito gli strumenti di allocazione per controllare la crescita dell'heap e anche questo sembra andare bene. Inoltre nessuna perdita di memoria.
Sono a corto di idee per testare/regolare ... Sarei davvero grato se qualcuno potesse aiutarmi con idee su che altro potrei testare, o forse indicando ciò che sto facendo male. O è proprio così, come dovrebbe funzionare ... che dubito ...
Grazie per qualsiasi aiuto.
EDIT
ho usato strumenti al profilo l'utilizzo della memoria con il modello Activity Monitor e il risultato mostrato in "Real Memory Usage" è la stessa di quella che viene stampato nella console con il get_free_memory
e la memoria non sembra mai essere rilasciata.
Sarebbe molto, molto meglio analizzare l'utilizzo della memoria in Strumenti anziché con quella funzione. Oltre a mostrare l'uso della memoria in momenti diversi, collegherà le allocazioni di memoria a specifiche linee di codice. –
Inoltre, sembra che ogni passaggio del ciclo interno elaborerà lo stesso record, poiché 'i' non cambia durante quel ciclo. –
Ciao Tom, Grazie per la risposta. Ho profilato l'app con il modello Allocations per la crescita dell'heap e il modello Leak, tutto sembra a posto ... Consiglieresti di usare il monitor Memory? Non ho molta esperienza con questo profiler. In realtà il 'i' sta aumentando di uno nel secondo ciclo (è necessario scorrere per vederlo, il codice non si adatta completamente alla cornice). Facendolo in questo modo, sono in grado di salvare i batch prima che finisca il blocco '@ autoreleasepool', che è un suggerimento che ho letto in Research 5) –