Заполнение памяти используемое программой.

ingener.solovyev
Offline
Зарегистрирован: 12.02.2013

leshak пишет:

попробуйте, перед client.stop() сделать

this.unregisterMethod("dispose",client);

если будет ругатся что "нет такого метода unregisterMethod", то это означает что у вас старый процессинг. Тогда вот так:

this.unregisterDispose(client);

 

Запустился первый вариант (процессинг сегодня переустанавливал т.к. винда чистая).

 

Кстати, а что это дает? Не хочется быть просто попугаем :)

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

А вы, все-таки, попробуйте рецепт из #49. Это попытка применить "решение" из Issue 1362 - processing - Recreating Client instance will cause an out-of-memory error, только не "внутри библиотеки", а "снаружи" (что-бы не разбиратся как их там в процессинге перекомпиливать, не ставить эклипс, SDK и т.п.).

 

ingener.solovyev
Offline
Зарегистрирован: 12.02.2013

leshak пишет:

А вы, все-таки, попробуйте рецепт из #49. Это попытка применить "решение" из Issue 1362 - processing - Recreating Client instance will cause an out-of-memory error, только не "внутри библиотеки", а "снаружи" (что-бы не разбиратся как их там в процессинге перекомпиливать, не ставить эклипс, SDK и т.п.).

 

Уже пробую, как 10 минут. Пока результат положительный (тьфу тьфу тьфу). Память держится в районе 23780КБ

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

>Кстати, а что это дает? Не хочется быть просто попугаем :)

Как я понял происходит примерно следующие:

Когда вы создаете объект Client, то первым параметром вы передаете ему this. В данный момент это ваш PApplet (вообщем главный объект вашего приложения, ваше окошко).

Этот клиент регистриует в PApplet свой метод Dispose (это метод который должен вызватся при разрушении объекта, что-бы освободить все ресурсы и т.п.). Что-бы PApplet, когда завершит свою работу (например пользователь нажал "закрыть"), мог сообщить всем кто попросил об этом (зарегистрировался у него) что "ребята, нас попросили с вещами на выход, собирайте манатки".

Вот.... :)

Теперь вам ваш объект Client - стал не нужен. Вы вызвали .stop(), сделали client=null. Но... PApplet про это ничего не знает. Client забыл ему сообщить что "все, меня уже зарахакирили, я уже все что нужно - освободил". Поэтому в PApplet по прежнему осталась ссылка на экземляр клиента. А раз на него "есть ссылка", то уборщик мусора, не может его удалить. Так как видит, что PApplet-у он еще может пригодится.

Мы же, за него сказали PApplet-у типа "забудь про этот объект" :) Не будет на него ссылки - он станет "никому не нужный", и GC, проходя мимо, отправит его в астрал. Освободит память.

Ну вот примерно так. 

Правда я выкинул из того что там советовали проверку if (host != null && thread != null)... лениво вникать нафиг она нужна. Тем более что эти host и thread - снаружи все равно скорее всего недоступны 

ingener.solovyev
Offline
Зарегистрирован: 12.02.2013

leshak пишет:

>Кстати, а что это дает? Не хочется быть просто попугаем :)

Как я понял происходит примерно следующие:

Когда вы создаете объект Client, то первым параметром вы передаете ему this. В данный момент это ваш PApplet (вообщем главный объект вашего приложения, ваше окошко).

Этот клиент регистриует в PApplet свой метод Dispose (это метод который должен вызватся при разрушении объекта, что-бы освободить все ресурсы и т.п.). Что-бы PApplet, когда завершит свою работу (например пользователь нажал "закрыть"), мог сообщить всем кто попросил об этом (зарегистрировался у него) что "ребята, нас попросили с вещами на выход, собирайте манатки".

Вот.... :)

Теперь вам ваш объект Client - стал не нужен. Вы вызвали .stop(), сделали client=null. Но... PApplet про это ничего не знает. Client забыл ему сообщить что "все, меня уже зарахакирили, я уже все что нужно - освободил". Поэтому в PApplet по прежнему осталась ссылка на экземляр клиента. А раз на него "есть ссылка", то уборщик мусора, не может его удалить. Так как видит, что PApplet-у он еще может пригодится.

Ну вот примерно так. 

Правда я выкинул из того что там советовали проверку if (host != null && thread != null)... лениво вникать нафиг она нужна. Тем более что эти host и thread - снаружи все равно скорее всего недоступны 

15 минут - полет нормальный. память так и есть 23770КБ.

Оставляю на ночь, утром отпишусь.

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

ingener.solovyev пишет:

Оставляю на ночь, утром отпишусь.

Да зачем так долго? Сделайте delay() поменьше на порядок. Вначале "без лечения" (что-бы убедится что проблема воспроизводится точно так же,тлько быстрее), а потом "с лечением".

ingener.solovyev
Offline
Зарегистрирован: 12.02.2013

leshak пишет:

ingener.solovyev пишет:

Оставляю на ночь, утром отпишусь.

Да зачем так долго? Сделайте delay() поменьше на порядок. Вначале "без лечения" (что-бы убедится что проблема воспроизводится точно так же,тлько быстрее), а потом "с лечением".

я дома, так что поменять не смогу ни чего. Только мониторить есть возможность. Кстати, пока все Ок, прошло уже 2.5 часа.

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

ingener.solovyev пишет:

я дома, так что поменять не смогу ни чего. Только мониторить есть возможность. Кстати, пока все Ок, прошло уже 2.5 часа.

А дома что мешает скачать и запустить  Processing? ;)

ingener.solovyev
Offline
Зарегистрирован: 12.02.2013

leshak пишет:

ingener.solovyev пишет:

я дома, так что поменять не смогу ни чего. Только мониторить есть возможность. Кстати, пока все Ок, прошло уже 2.5 часа.

А дома что мешает скачать и запустить  Processing? ;)

дома любимая супруга и две дочери, не до процессингов.

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

ingener.solovyev пишет:

дома любимая супруга и две дочери, не до процессингов.

Да. Это тоже неплохо память умеет переполнять. Тоже иногда папу могут высадить в состояние SystemOverflow :) И никакой unregisterMethod тут не поможет :)

ingener.solovyev
Offline
Зарегистрирован: 12.02.2013

Отчитываюсь.

Работа Processinga в режиме Клиент на протяжении 16 часов проблемм увеличения памяти не выявила.

Выкладываю работоспособный минимализированный код.

import processing.net.*;



//Связь с Ethernet клиентом и сервером
int arduino_port = 80; // порт сервера подключения
String IP_arduino = "127.0.0.1"; //IP адрес сервера к которому подключаетесь.
//Символ до которого будем считывать данные
int list;
void setup()
{
  size (100, 100);
  background(255);
}

void draw()
{
  //Прием данных с ардуино
  Client client;
  client = null;
  client = new Client(this, IP_arduino, arduino_port);
  delay (1000);
  client.write ("1");
  delay (1000);
try {  
  while (client.available ()>0)
    {
      list = client.read();
      this.unregisterMethod("dispose",client);
      client.stop();
    }
  } 
  catch (Exception e) {
    list = 0;
  }
  background(255);
  textSize(32);
  fill (0);
  if (list == 0) {
    println ("Null");
    text ("Null", 10, 50);
  }
  if (list != 0) {
    println (list);
    text (list, 10, 50);
  }

client = null;
System.gc();
}

 

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

Строка 20 - 100% лишняя. Убрав ее строки 19 и 21-ть можно объединить в одну.

Строка 48  - 95% лишняя. Это мы уже перебирали "от отчаяния". По идее оно само, по выходу из функции, должно понимать что "переменная больше не нужна" и уничтожать ее. Так как было не понятно почему оно ее не уничтожает, то мы фактически сделали "повторяем для дебилов: переменная нам не нужна". Но, так как мы нашли почему переменная не удалялась, то скорее всего явное client=null - уже не нужно.

Строка 49 - 60% лишняя. По идее, System.gc(), время от времени вызывается автоматически сам. Это было опять-таки "от отчаяния". Поэтому - можно попробовать убрать. В результате потребление памяти может начать чуть-чуть расти, но потом - освободится. Вообщем "мусорщик" будет вызыватся чуть-чуть реже. Зато - без усилий с нашей стороны.

Строка 29,30 должны идти после while, а не внутри его. Просто "как-то не логично". По идее нам же нужно "вычитать данные", а потом обрывать коннекшн. А так у вас получилось "прочитали один байт и сразу оборвали". Если вы "так и хотели", то зачем там while?  Более логичным был-бы if 

ingener.solovyev
Offline
Зарегистрирован: 12.02.2013

leshak пишет:

Строка 20 - 100% лишняя. Убрав ее строки 19 и 21-ть можно объединить в одну.

Строка 48  - 95% лишняя. Это мы уже перебирали "от отчаяния". По идее оно само, по выходу из функции, должно понимать что "переменная больше не нужна" и уничтожать ее. Так как было не понятно почему оно ее не уничтожает, то мы фактически сделали "повторяем для дебилов: переменная нам не нужна". Но, так как мы нашли почему переменная не удалялась, то скорее всего явное client=null - уже не нужно.

Строка 49 - 60% лишняя. По идее, System.gc(), время от времени вызывается автоматически сам. Это было опять-таки "от отчаяния". Поэтому - можно попробовать убрать. В результате потребление памяти может начать чуть-чуть расти, но потом - освободится. Вообщем "мусорщик" будет вызыватся чуть-чуть реже. Зато - без усилий с нашей стороны.

Строка 29,30 должны идти после while, а не внутри его. Просто "как-то не логично". По идее нам же нужно "вычитать данные", а потом обрывать коннекшн. А так у вас получилось "прочитали один байт и сразу оборвали". Если вы "так и хотели", то зачем там while?  Более логичным был-бы if 

import processing.net.*;



//Связь с Ethernet клиентом и сервером
int arduino_port = 80; // порт сервера подключения
String IP_arduino = "127.0.0.1"; //IP адрес сервера к которому подключаетесь.
int list;
void setup()
{
  size (100, 100);
  background(255);
}

void draw()
{
  //Прием данных с ардуино
  Client client = new Client(this, IP_arduino, arduino_port);
  delay (1000);
  client.write ("1");
  delay (1000);
try {  
  while (client.available ()>0)
    {
      list = client.read();
    }
  this.unregisterMethod("dispose",client);
  client.stop();
  } 
  catch (Exception e) {
    list = 0;
  }
  background(255);
  textSize(32);
  fill (0);
  if (list == 0) {
    println ("Null");
    text ("Null", 10, 50);
  }
  if (list != 0) {
    println (list);
    text (list, 10, 50);
  }

System.gc();
}

 

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

Если я правильно понимаю, то эти строки поставлены наоборот:

this.unregisterMethod("dispose",client);
client.stop();

Мы сначала "освобождаем" объект, а потом стопим уже освобожденный объект. Я допускаю, что это работает, но логика непонятна. Во всяком случае мне непонятна.

 

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

kisoft пишет:

Мы сначала "освобождаем" объект, а потом стопим уже освобожденный объект. Я допускаю, что это работает, но логика непонятна. Во всяком случае мне непонятна.

Нет. Вы не поняли "почему проблема". unregisterMethod - не "освобождает объект". А "особождает ссылку" внутри PApplet.

Смотрите. А нас есть два объекта. a и b.  Объект a имеет ссылку на объект b. Где-то раньше в коде было сделанно что-то типа a.extReff=b;

Потом мы решили что b нам больше не нужен. И сделали b=null; Обнулили "переменную b". Но, на сам объект по прежнему осталась живая ссылку внутри объекта a. И мусорщик никак не может удалить объект с концами. Поэтому мы делаем что-то типа

a.extReff=null; // сказали объекту a забыть о существовании b
b.stop();// сами еще поработали с объектом
b=null; // нам тоже уже не нужна ссылка на этот объект

// все раз "никому он не нужен", мусорщик его может его выкинуть в помойку.

 

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

leshak пишет:

Нет. Вы не поняли "почему проблема". unregisterMethod - не "освобождает объект". А "особождает ссылку" внутри PApplet.

Переформулирую, почему нужно сначала "отцепить" объект, а потом его остановить, почему не наоборот?

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

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

Потому что метод unregisterMethod принимает объект который нужно "отцепить". Если вы передадите ему null, то как он узнает "кого отцеплять"? Он же не про один объект помнит.

Но, в принципе, можно и после client.stop() попробовать вызвать. Просто перед - оно ближе к естественной логики. По хорошему сам client должен был сделать отцепляние. Внутри stop(). Мы как бы "сделали за него работу". "Дописали" начало метода stop()

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

А еще, внутри метода stop() вызывается dispose(). То есть фактически объект приходит "в нерабочее состояние". Лично мне было проще вызвать ungregister с еще "заведомо правильным объектом", чем выяснять в деталях реализацию unregisterMethod, может он в процессе де-регистрации еще что-то пытается вызвать у client()-та, к примеру сообщить ему "все чувак,  я больше за тобой не слежу". И неведомо как client у которого уже вызвали stop() сможет это корректно обработать.

Если любопытно - можете попрбовать вызвать после stop() (но в любом случае до client=null, если вы его делаете).

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

Ок, останемся при своих. Спасибо за информацию, тема мне больше не интересна

toc
Offline
Зарегистрирован: 09.02.2013

>> client.stop();

при выполнении метода экземпляра "stop" (instance method) ccылка на экземпляр "сlient" не может измениться, в том числе не может об-null-иться

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

toc пишет:

>> client.stop();

при выполнении метода экземпляра "stop" (instance method) ccылка на экземпляр "сlient" не может измениться, в том числе не может об-null-иться

Хм... за кого вы меня держите? :)

А вот может быть примерно такой unsubscribeMethod()

void unsibscribeMethod(Object obj){

   if(obj is IUnsubscrible){ 
      ((IUnsubscrible))obj).notifyAboutUnsubscribe(); // сообщаем объекту что он больше не подписан
   }

   // удаляем объект из внутренних ссылок
   internalRefs.remove(obj);
   .....

}

Далее.

А в client.notifyAboutUnsubscribe() используются какие-то внутрении поля, разрушаемые при вызове stop()/dispose()

В таком случае - вызов после stop() - закончится проблемами.

Согласен, такой "вычурный код", куча "взаимных callback" - это фигня архитектуры. Но, увы, и не такое видали.

Так что я предпочитаю - не хмурить мозг "как оно там на самом деле", и что-то делать с объектом только в заведомо рабочем состоянии. Если я вижу что у объекта уже был вызван dispose() - то я инстинктивно ставлю себе в голове галочку "все, больше с этим объектом работать нельзя". Хотя может быть он бы и мог еще "потрепыхатся", зачем мне такие риски, тем более в ситуации когда код запускается другим человеком удаленно и итерация проверки занимает часы/дни? Разве не логично что я выберу "самый безопастный вариант"?