Как очистить память от локальной переменной

lexa217
Offline
Зарегистрирован: 08.12.2012

Как очистить память от локальной переменной типа String, которая генерируется динамически внутри функции? после выполнения функции эта информация больше не нужна, а память, отведенная под эту строку, остается занятой.

Borland
Offline
Зарегистрирован: 17.05.2012

 

память под локальные переменные отводятся в стеке и при выходе из функции пропадает

что касается класса String , то это обьект, и ИМХО задача програмиста  удалять его , если он не нужен

 

 

lexa217
Offline
Зарегистрирован: 08.12.2012

как правильно удалить этот тип объекта?

delete имя_переменной; выдает ошибку.

если переменная типа String глобальная, можно ли ее удалить?

если сделать имя_переменной=""; то переменная становится пустой, но память, занятая ею раньше остается забитой. можно ли очистить занимаемую ею память?

Borland
Offline
Зарегистрирован: 17.05.2012

Полагаю так

Если вы в  функции создадите

String A;

без конструктора new , то заботится не о чем , и конструктор и деструктор будет создан при компиляции, обьект будет уничтожен при выходе из функции, Не уверен но возможно весь обьект так же будет расположен в стеке, поэтому заботится не о чем, более знающие поправят.

Что касается String то нет под рукой обьявления класса, нахожусь не на работе. Но если создавать String конструктором new то надо вызывать деструктор, в котором описано удаление буфера

 

Глобальный обьект ( такой же String A; но вне функции )полагаю удалить нельзя он при компиляции описывается в памяти,нечто вроде static. Подозреваю что присвоение стрингу A=""  даст освобождение памяти. не  обьекта, но на величину буфера обекта под строку.

косвенное подтверждение всего  сказанного мною кроме последнего -

http://stackoverflow.com/questions/9805829/arduino-c-destructor

 

 

 

 

leshak
Offline
Зарегистрирован: 29.09.2011

Borland пишет:

Глобальный обьект ( такой же String A; но вне функции )полагаю удалить нельзя он при компиляции описывается в памяти,нечто вроде static. Подозреваю что присвоение стрингу A=""  

Если не поможет (думаю это так, не нашел вколде что бы размер буффера уменьшался, вижу только увеличение, если не хватает), то можно попытася A=0;

Внутри Wstring.cpp это выглядит так:

String & String::operator = (const char *cstr)
{
	if (cstr) copy(cstr, strlen(cstr));
	else invalidate();
	
	return *this;
}

Что, по идее должно вызвать invalidate();

Который, в свою очередь:

void String::invalidate(void)
{
	if (buffer) free(buffer);
	buffer = NULL;
	capacity = len = 0;
}

то есть "то что нам нужно".

Но, в приличных конторах, за такие финты дают в глаз :) Так как совершенно не очевидно что делает A=0;, во вторых мы полагаемся на конкретную реализацию интерфейса (про которую не должны ничего знать). Если в будущем поменяют внутреню реализацию метода присваивания (имеют полное право) и он перестанет при нуле вызывать методо invalidate(), то тот кто будет чинить образовавшийся баг - поседеет.

Уж лучше полезть сделать свой форк от WString.h и добавить метод с названием типа resetBuff()  (а в нем вызывать invalidate()). В таком случае если будет апдейт, то тот кто будет обновлять форк, будет вынужден посмотреть не исчезли метод invalidate() и обязан убедится что он продолжает делать то же самое.

Ну а еще, как вариант, забить болт на String и пользоватся обычными сишными строками, самому выделить, самому освобождать и не тянуть за собой громадное количестве методов, которые редко когда используются все сразу :)

 

lexa217
Offline
Зарегистрирован: 08.12.2012

Спасибо за развернутый ответ =) завтра попробую сделаь с присвоением нуля.

С локальными переменными теперь понятно, а вот возможно ли уменьшить размер глобальной переменной?

с присвоением "" память не освобождается.

в скетче читаю в стринг  (глобальную) ком-порт, для последующего разбора строки. если приходит много данных, то остается занятая память под переменную и очистить ее пока не удается.

 

leshak
Offline
Зарегистрирован: 29.09.2011

Ну можно еще так попытася извратится - пронаследоватся от String


class ResetableString:public String{
  
  public:
   ResetableString(const char *cstr):String(cstr){}
   void  reset(){
     invalidate();
   }
};

ResetableString A=ResetableString("ABC CBA"); // глобальная строка

void setup(){
  Serial.begin(57600);
  Serial.print("Before reset len=");Serial.println(A.length());
  A.reset();
  Serial.print("After reset len=");Serial.println(A.length());

}

void loop(){
}

Выводит 

Before reset len=7
After reset len=0

Но, IMHO сликом много возни (потом еще операторы доопределять что-бы было удобно и т.п.)

lexa217
Offline
Зарегистрирован: 08.12.2012

Спасибо, интересное решение) завтра буду пробовать.  Но возни действительно много.

 

А может быть Вы сможете объяснить мне как работает эта функция?

int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

долго думал, но так и не смог понять

leshak
Offline
Зарегистрирован: 29.09.2011

Вроде работает с нулем.

String A=String("ABC CBA"); // глобальная строка
void setup(){
  Serial.begin(57600);
  Serial.print("Before reset len=");Serial.println(A.length());
  Serial.print("Before Free mem=");Serial.println(availableMemory());
  A=0;
  Serial.print("After reset len=");Serial.println(A.length());
   Serial.print("After Free mem=");Serial.println(availableMemory());
}

void loop(){
}


int availableMemory() {
  int size = 8*1024; // Use 2048 with ATmega328
  byte *buf;

  while ((buf = (byte *) malloc(--size)) == NULL)
    ;

  free(buf);

  return size;
}

 

Выдает

Before reset len=7
Before Free mem=7345
After reset len=0
After Free mem=7355

Так что память явно освобожадется, но это ОЧЕНЬ плохой стиль программирования :)

 

leshak
Offline
Зарегистрирован: 29.09.2011

lexa217 пишет:

С локальными переменными теперь понятно, а вот возможно ли уменьшить размер глобальной переменной?

Да с локальными вообще ничего мудрить не нужно. При выходе из функции вызовется деструктор, а он вызовет free - и все. Никаких движений по этому поводу от нас - не требуется.

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

 

leshak
Offline
Зарегистрирован: 29.09.2011

lexa217 пишет:

Спасибо, интересное решение) завтра буду пробовать.  Но возни действительно много.

 

А может быть Вы сможете объяснить мне как работает эта функция?

int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

долго думал, но так и не смог понять

 Видите ключевое слово extern? Это значит что __heap_start и __brkval обявлены гдето-то в другом месте (и кто-то их должен был перед этим заполнить) .  Насколько я понимаю это куча и стек.

И кто-то их должен был заполнить раньше. Либо их кто-то заполняет внутрях ардуино-библиотек-дебрей, либо... нужно воспользоватся функцией типа check_mem(). Подозреваю что код вы утащили с http://playground.arduino.cc/Code/AvailableMemory, там она приведена чуть выше (правда имена переменных чуток другие).

Но... у меня она простым copy-paste не заработала... я и не стал вникать в ее тонкости. Хватило avaliableMemory()  (я ее использовал в прошлом посте).

leshak
Offline
Зарегистрирован: 29.09.2011

Кстати, иногда еще полезно начинать знакомство с форумом начиная с прекрепленных веток

Вставка программного кода в тему/комментарий

Это намек :)

lexa217
Offline
Зарегистрирован: 08.12.2012

С кодом исправлюсь) а эта функция попалась мне где-то давно и я ее часто использую. 

то, что эти переменные берутся извне - это я понимаю. а вот что делается здесь - понять пока не могу.

  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);

в особенности, что значит &v-...

ведь значение v ранее не определено.

мне кажется, что работает так, но я не уверен. если не прав, то поправьте =)

заводится новая переменная v и берется ее адрес &v. из него вычитается адрес кучи или значение стека.

но непонятно почему значение стека может бть равным нулю и почему вычитается только одно значение, а не сумма обоих?

Borland
Offline
Зарегистрирован: 17.05.2012

lexa217 пишет:

С

int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

 

возвращает разницу от текущего стека до последнего свободного места памяти еще не использованного функциями malloc.

полагаю более "врущая" чем freememory у Leshak . но позволяет оценить чего с памятью

lexa217
Offline
Зарегистрирован: 08.12.2012

Спасибо) завтра попробую сравнить результаты работы этих функций)

 

leshak
Offline
Зарегистрирован: 29.09.2011

lexa217 пишет:

С кодом исправлюсь) а эта функция попалась мне где-то давно и я ее часто использую. 

то, что эти переменные берутся извне - это я понимаю. а вот что делается здесь - понять пока не могу.

  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);

в особенности, что значит &v-...

ведь значение v ранее не определено.

мне кажется, что работает так, но я не уверен. если не прав, то поправьте =)

заводится новая переменная v и берется ее адрес &v.

Вы же сами и ответили. Значит важно не значение v, а где именно оно расположено в памяти. Подозерваю что и объявлена она в самом конце функции не спроста.

 

lexa217 пишет:

но непонятно почему значение стека может бть равным нулю и почему вычитается только одно значение, а не сумма обоих?

А фиг его значт что в этом __brkval на самом деле лежит. Может в разных камнях/компяляторах там все тот же адрес кучи :) Типа если он есть - ищем в одной переменной, если нет - смотрим может она по другому называется.

Не знаю, если честно - лень уже разбиратся. Я вообще с C/C++ одновременно с ардуиной началал знакомится. Мой родной -  C#.  А там забота о памяти - дело компилятора, а не моя :)

Borland
Offline
Зарегистрирован: 17.05.2012

lexa217 пишет:

в особенности, что значит &v-...

ведь значение v ранее не определено.

Адрес v и есть значение стека

какое значение v принимает - по барабану

lexa217
Offline
Зарегистрирован: 08.12.2012

leshak пишет:

Не знаю, если честно - лень уже разбиратся. Я вообще с C/C++ одновременно с ардуиной началал знакомится. Мой родной -  C#.  А там забота о памяти - дело компилятора, а не моя :)

 

Вот я тоже на C++ одновременно с ардуиной перешел) чаще на javascript и delhpi писать приходится. дельфи - не та степь, а на джаваскрипт тоже о памяти в последнюю очередь заботишься)

но вот два килобайта в ардуине заставили заморочиться... каждый байт считать приходится...

leshak
Offline
Зарегистрирован: 29.09.2011

lexa217 пишет:

дельфи - не та степь

Из всего выше перечисленного, помоему как раз самая близкая степь. По воспоминаниям детства (ТурбоПаскакаль 3.0 и дальше, Delphi XXX) там как раз были и указатели и освобождение/выделения памяти, и стек... но все прочно забылось за 15-ть лет :)

А вот javascript - действительно не та. Не типизированый. Интерпретируемый, а не компилируемый (хотя идет к тому), наследование - прототипное.... вообщем кроме C-подобного синтаксиса (а у кого его щас нет?) - ничего общего.

Дальше него, IMHO, будет только что-то типа Prolog (эх... как жаль что не пошел он в массы).

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Вы тут хорошо во всём разобрались :)
Освобождать глобальные переменные - уже плохой стиль и самое правильное - уйти от этого. А для меня С++ самый родной, шарп не люблю. Сравнивать не будем. Каждой задаче - свой язык

leshak
Offline
Зарегистрирован: 29.09.2011

kisoft пишет:
Вы тут хорошо во всём разобрались :) Освобождать глобальные переменные - уже плохой стиль и самое правильное - уйти от этого.

Если быть "очень строгим", то "глобальные переменные" вообще зло. Засоряется namespace. Функции имеют hidden эффекты и т.п. Спасает только то, что в дуине памяти мало, нет маштабных проектов где это могло бы превратится в ад.

kisoft пишет:

А для меня С++ самый родной, шарп не люблю. Сравнивать не будем. Каждой задаче - свой язык

Все верно :)  Хотя есть аналоги дуины и с C# внутре :) А в будущем, подозреваю, с падением цены на мощные процы будут массово "мигать диодом на php" :) (уже пошли первые ласточки). И зубовный скрежет "старой школы" :)

Но вы правы, все зависит от задачи. Когда нужна тяжелая бизнес-логика (просто куча правил и т.п.), когда "добавить 2 гига памяти" стоит меньше чем час работы программера ищущего "где же у нас память течет" - C# рулит. А в еще более серьезных проектах даже свои еще более высокоуровненвые язычки пишутся (DSL) (и еще более прожорливые к ресурсам).

А вот когда "байты держат за горло", тут уж без C/C++ не обойтись :) Или нужен "жесткий realtime", то от C# будут только крики "дайте мне какие-нибудь костыли" :)

А про "люблю" - моя любовь дества - Prolog. Хотя в "боевой задаче" за всю жизнь всего один раз удалось уговорить его заюзать. Даже майка есть "все равно Prolog не брошу, потому что он хороший" :)

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Как же хорошо, что некоторые темы не требуют отсылать к элементарным вещам, прямо отдыхаешь от "помогите новичку" :)

Хотя я тоже новичок и Leonardo мой мне нравится хотя бы тем, что заставляет лазить по даташитам, докам, инету и искать почему пины другие в сравнении с Uno и т.п. Пора открыть тему, как измерить количество гигабайт, которое я перевернул в интернете, когда искал ответ на интересующий меня вопрос :)

Всё, я в оффтоп залез :) ухожу

Кстати, только что пришла аналогия про глобальные переменные. Купил билет, пришел на сеанс, а тебя по середине сеанса выгоняют, у нас, типа, мест не хватает!!! ;) Бедные глобальные переменные!

 

leshak
Offline
Зарегистрирован: 29.09.2011

kisoft пишет:

Кстати, только что пришла аналогия про глобальные переменные. Купил билет, пришел на сеанс, а тебя по середине сеанса выгоняют, у нас, типа, мест не хватает!!! ;) 

Не, это скорее уже из области многопоточности и lock-ов:

Acess denay. The process cannot access the object ... because it is being used by another process. Try again later.

Главное что-бы потом не последовало "kill process" :)

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

В AIX прикольно сделано, если приложение просит память а её нет, он (AIX) приложению говорит, что, типа, отстань, памяти мало. Сначала культурно говорит, потом повышая тон. Но если приложение продолжает настойчиво требовать память, это приложение просто отстреливается :) У нас так DB2 работала, потом всё дружно сдохло, сервер работал, но на пинги не отвечал, а всё из-за кривых рук, не настроили DB2.

Надо открыть пятый раздел в форуме - "Охотники на привале" :)

 

lexa217
Offline
Зарегистрирован: 08.12.2012

Всем спасибо за ответы)

сегодня, наконец, добрался до проекта. память освободил другим образом.

Оказывается, запись   lcd.print("blablabla"); занимает место в оперативке. я думал, что она занимает место только в прошивке.

сократил текстовые строки и заменил одинаковые куски переменными. выиграл кучу памяти, так что удалять переменные больше не нужно)

 

leshak
Offline
Зарегистрирован: 29.09.2011

lexa217 пишет:

Оказывается, запись   lcd.print("blablabla"); занимает место в оперативке. я думал, что она занимает место только в прошивке.

Естественно :)

Ключевое слово PROGMEM  вам поможет. Это как раз и есть указание компилятору "не нужно копировать ее в RAM" при запуске скетча (но потом нужно озаботится "чтением" когда строка понадобится). Так же слово PSTR погуглить нужно.

P.S. Если бы сразу сказали в чем именно дело :) Изначально задача была сформулированно как "_динамически_ формируем длинные строки, от которых желательно избавлятся", а не "как бы поудачней их хранить, а то памяти мало".

lexa217
Offline
Зарегистрирован: 08.12.2012

Сначала я хотел и с их размером разделатсья. потом понял, что мне это ничего не даст. просто стал контролировать их размер.

но места все равно было мало (строка String занимает по два байта на каждый символ). эти строки я получаю от GSM модуля и разбираю их на текст (команда) и значения (число). разбирать массив char намного сложнее.

я думал, что у меня оптимально использована память основного проекта, поэтому не заморачивался о хранении всех остальных переменных.

когда стал искать, что бы удалить, то попробовал сократить количество слов, выводящихся на экран или в ком порт. в общем сильно удивился)..

про прогмем немного знаю, но даже не думал, что мне может понадобиться переносить все слова меню в прогмем переменные...

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Во! А я то думал, чего это у меня прога не пашет! PROGMEM еще и читать надо правильно!

Yes!

Понастроили тут, панимаэшь, памятей разных понавтыкали, блин! :)

 

leshak
Offline
Зарегистрирован: 29.09.2011

kisoft пишет:

Во! А я то думал, чего это у меня прога не пашет! PROGMEM еще и читать надо правильно!

Yes!

Я себе вот такую парочку накорябал:

// аналоги Serial.print и Serial.println, но строки берут из флеша.
void print (PGM_P s) {
        char c;
        while ((c = pgm_read_byte(s++)) != 0)
            Serial.print(c);
    }

void println(PGM_P s){
	print(s);
	Serial.println();
}

 

Но, при разработке специально пишу Serial.println("BLA-BLA");  пока памяти хватает.

А как не хватает, то у меня сразу есть "кандидат для оптимизации", заменяю

Serial.println("BLA-BLA"); на println(PSTR("BLA-BLA"));   и имею профит по памяти.

Так сказать искуственно создаю себе "звоночек-предупреждение".

Можно себе сделать "помогалку".

объявить два дефайна

#define PRINTLN(val)  Serial.println(val)
//  #define PRINTLN(val) println(PSTR(val))

В коде писать PRINTLN("BLA-BLA") , а потом, когда понадобится закоментить первый, раскоментить второй и будет юзатся флешь, а не RAM. Не нужно будет по всему коду лазить "переделывать".

Ну и "без фанатизма". Помним что сама ссылка на флешь, тоже память жрет. Поэтому строчки из одного-двух символов нет смысла в такую беду оборачивать. Только длинные.

kisoft пишет:

Понастроили тут, панимаэшь, памятей разных понавтыкали, блин! :)

Это называется "работаем близко к железу". В этом же и прелесть дуины :) 

В конце концов :

Цитата:

  - С тех пор, - продолжал Арамис, - моя жизнь протекает очень приятно. Я начал писать поэму односложными стихами. Это довольно трудно, но главное достоинство всякой вещи состоит именно в ее трудности. 

Если не согласны с ним - смотрите в сторону netduino, меги и проч. Вообщем благо прогресс вполне допускает взять более мощьное железо и не морочится оптимизацией. Более того очень часто это более выгодный путь :)

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Я пока что в творческом поиске, активно изучаю/вспоминаю, объем памяти не парит. Главное сейчас научить себя и постичь тонкости, а потом всё просто, взял, да написал, что нужно. Главное не начинать с космического аппарата и довести все планы до реализации.