Рекомендации по управлению использованием ОЗУ в высокоуровневых приложениях

Хотя ос Azure Sphere использует ядро Linux в качестве основы, важно помнить, что вы по-прежнему пишете приложения для встроенного устройства со значительными ограничениями ОЗУ. Применение хороших внедренных методик программирования поможет вам создавать надежные приложения Azure Sphere.

Важно

Чтобы получить точные сведения об использовании ОЗУ для приложения, важно запустить приложение без отладки. Запуск приложения в отладчике приведет к завышению использования ОЗУ, так как ОЗУ, потребляемая сервером отладки, будет включена в сообщаемую статистику использования ОЗУ. Дополнительные сведения о статистике памяти для приложений, работающих на подключенном устройстве, см. в разделе Использование памяти в высокоуровневых приложениях.

Ниже приведены рекомендации по выполнению следующих рекомендаций.

  • Выделите память заранее (в идеале — статически) и по возможности оставьте ее выделенной на время существования приложения. Это значительно повысит детерминированность использования ОЗУ приложения, а также снизит риск увеличения объема памяти и фрагментации в течение всего времени существования приложения.
  • Если необходимо динамическое выделение:
    • Попробуйте свести к минимуму частоту выделения памяти кучи и освобождений, выполняемых приложением, чтобы снизить риски фрагментации памяти кучи, например, используя методы выделения блоков или пула памяти.
    • Просмотрите страницы стека и, когда это возможно, обтекайте вызовы в malloc() с вызовами , memset() чтобы принудительно зафиксировать страницы. Это помогает гарантировать, что если выделение приведет к превышению лимита ОЗУ для приложения, ОС немедленно и предсказуемо завершит работу. Ожидание доступа к выделенным страницам приведет к риску отложенного сбоя нехватки памяти, который сложнее воспроизвести и диагностировать.
    • Включите отслеживание выделения памяти кучи в режиме разработки.
  • Избегайте использования Log_Debug с большими строками и удаляйте эти вызовы (например, с #ifdefпомощью ), если не в режиме разработки. Log_Debug приводит к выделению временных буферов, что приводит к внезапным всплескам использования ОЗУ при использовании с большими строками.
  • По возможности используйте API EventLoop для периодических асинхронных задач (таких как взаимодействие с периферийными устройствами) вместо создания потоков. Создание потоков приводит к тому, что ядро Linux выделяет дополнительную память, приписываемую вашему приложению. Это снижает детерминированность приложения, так как увеличивает вероятность переключения планировщика ОС между несколькими отдельными операциями, которые могут привести к превышению лимита ОЗУ приложения. Многие примеры приложений Azure Sphere, например GPIO_HighLevelApp, демонстрируют использование EventLoop.
  • Избегайте преждевременного использования кэшей памяти для значений, которые можно перекомпьютерировать во время выполнения.
  • При использовании libcurl:
    • Настройте максимальный размер буфера сокетов при использовании libcurl. ОС Azure Sphere выделяет буферы сокетов, которые относятся к использованию ОЗУ приложения. Уменьшение этих размеров буфера может быть хорошим способом сокращения объема ОЗУ приложения. Обратите внимание, что слишком малые буферы сокетов негативно повлияет на производительность libcurl. Вместо этого настройте максимальный размер буфера для своего сценария:

          static int sockopt_callback(void* clientp, curl_socket_t curlfd, curlsocktype purpose)
          {
              int size = /*specify max buffer sizes here (in bytes)*/
              int size_size = sizeof(size);
              setsockopt(curlfd, SOL_SOCKET, SO_SNDBUF, &size, &size_size);
              setsockopt(curlfd, SOL_SOCKET, SO_RCVBUF, &size, &size_size);
              return CURL_SOCKOPT_OK;
          }
      
          // Place the following along with other calls to curl_easy_setopt
          curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, &sockopt_callback);
      

      См. документацию по libcurl CURLOPT_SOCKOPTFUNCTION .

      • Параметры CURLOPT_BUFFERSIZE и CURLOPT_UPLOAD_BUFFERSIZE более высокого уровня можно настроить аналогичным образом.

      • Libcurl также поддерживает переопределение внутренних функций памяти с помощью curl_global_init_mem и передачи функций обратного вызова для malloc, free, realloc, strdupи calloc. Эта функция позволяет отслеживать динамические выделения или даже изменять поведение. Например, можно заранее выделить пул памяти, а затем использовать эти обратные вызовы для выделения памяти libcurl из этого пула. Это может быть эффективным методом для установки ограждений и повышения детерминированности приложения. Дополнительные сведения об использовании этих обратных вызовов см. в документации по libcurl по curl_global_init_mem.

        Примечание

        Этот механизм обратного вызова охватывает не все выделения памяти, вызванные libcurl, а только те, которые непосредственно выполняет сам libcurl. В частности, выделения, сделанные wolfSSL под ним, не отслеживаются.