Импорт основных данных - не освобождение памяти

Мой вопрос в том, что Core Data и память не будут выпущены. Я делаю процесс синхронизации, импортируя данные из WebService, который возвращает json. Я загружаю в память данные для импорта, циклирования и создания NSManagedObjects. Импортируемые данные должны создавать объекты, имеющие отношения к другим объектам, всего около 11 000. Но для того, чтобы изолировать проблему, я сейчас создаю элементы первого и второго уровня, оставляя связь вне, это 9043 объекта.

Я начал проверять объем используемой памяти, потому что приложение терпело крах в конце процесса (с полным набором данных). Первая проверка памяти после загрузки в память json, так что измерение действительно учитывает только создание и вставку объектов в Core Data. Для проверки используемой памяти используется этот код (источник)

-(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));
    }
}

Моя настройка:

  • 1 Постоянный координатор магазина
  • 1 Main ManagedObjectContext (MMC) (NSMainQueueConcurrencyType используется для чтения (только чтения) данных в приложении)
  • 1 Background ManagedObjectContext (BMC) (NSPrivateQueueConcurrencyType, undoManager установлен на nil, используется для импорта данных)

BMC не зависит от MMC, поэтому BMC не является дочерним контекстом MMC. И они не разделяют какой-либо родительский контекст. Мне не нужно BMC уведомлять изменения в MMC. Поэтому BMC необходимо создавать/обновлять/удалять данные.

Plaform:

  • iPad 2 и 3
  • iOS, я тестировал установку цели развертывания на 5.1 и 6.1. Нет разницы.
  • XCode 4.6.2
  • ARC

Проблема: Импортируя данные, используемая память не перестает увеличиваться, и iOS, похоже, не может разрядить память даже после завершения процесса. Что, в случае увеличения образца данных, приводит к предупреждению памяти и после закрытия приложения.

Исследование:

  • Документация Apple

  • Хорошее описание пунктов, которые нужно иметь в виду при импорте данных в Core Data (qaru.site/info/58752/...)

  • Выполненные тесты и анализ выпуска памяти. Кажется, у него такая же проблема, как у меня, и он отправил отчет Apple Bug, но от Apple еще не ответил. (Источник)

  • Импорт и отображение больших наборов данных (Источник)

  • Указывает лучший способ импортировать большой объем данных. Хотя он упоминает:

    "Я могу импортировать миллионы записей в стабильной 3 МБ памяти без вызов - reset."

    Это заставляет меня думать, что это может быть как-то возможно? (Источник)

Тесты:

Пример данных: создание всего 9043 объектов.

  • Отключил создание отношений, поскольку в документации говорится, что они "дороги"
  • Никакой выборки не выполняется

Код:


- (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:

Он идет от 16,97 МБ до 30 МБ, после синхронизации он сокращается до 28 МБ. Повторяя вызов get_memory каждые 5 секунд, память сохраняется на уровне 28 МБ.

Другие тесты без везения:

  • воссоздание постоянного хранилища, как указано в исследовании 2) не имеет эффекта
  • проверено, чтобы поток немного подождал, чтобы восстановить восстановление памяти, пример 4)
  • установочный контекст для nil после всего процесса
  • Выполнение всего процесса без сохранения контекста в любой момент (потере информации). Это на самом деле дало в результате меньшее количество памяти, оставив ее на уровне 20 МБ. Но он все еще не уменьшается и... Мне нужна информация, хранящаяся:)

Возможно, мне что-то не хватает, но я действительно много тестировал, и после выполнения рекомендаций я ожидал, что память снова уменьшится. Я запустил инструменты Allocations, чтобы проверить рост кучи, и, похоже, это тоже хорошо. Также нет утечек памяти.

У меня заканчиваются идеи по тестированию/настройке... Я был бы очень признателен, если бы кто-нибудь мог помочь мне с идеями о том, что еще я мог бы проверить, или, возможно, указывая на то, что я делаю неправильно. Или это просто так, как это должно работать... что я сомневаюсь...

Спасибо за любую помощь.

ИЗМЕНИТЬ

Я использовал инструменты для профилирования использования памяти с помощью шаблона Activity Monitor, и результат, показанный в "Реальное использование памяти", такой же, как тот, который печатается в консоли с помощью get_free_memory, и память все еще никогда не кажется чтобы освободиться.

Ответ 1

Хорошо, это довольно неловко... Зомби были включены на Схеме, по аргументам, которые они отключили, но по диагностике "Включить объекты зомби" был проверен...

При выключении поддерживается стабильная память.

Спасибо тем, кто прочитал вопрос и попытался его решить!

Ответ 2

Мне кажется, что ключевое отключение вашего любимого источника ( "3 МБ, миллионы записей" ) - это упоминание о пакете - помимо отключения менеджера отмены, который также рекомендуется Apple, и очень важно).

Я думаю, что здесь важно, что эта партия должна применяться и к @autoreleasepool.

Это недостаточно для слива пула авторефератов каждые 1000 итераций. Вам нужно фактически сохранить MOC, а затем слить пул.

В своем коде попробуйте поместить второй @autoreleasepool во второй цикл цикла. Затем отрегулируйте размер партии для точной настройки.

Я провел тесты с более чем 500 000 записей на оригинальном iPad 1. Размер одной строки JSON был близок к 40 МБ. Тем не менее, все работает без сбоев, а некоторые настройки даже приводят к приемлемой скорости. В моих тестах я мог претендовать на приложение. 70 МБ памяти на оригинальном iPad.