Тема: Удаление примитивов и очистка памяти

Здравствуйте, уважаемые дамы и господа! Я новичок в ObjectArx, поэтому заранее прошу прощения, если мои вопросы покажутся некорректными либо наивными. Два похожих вопроса.
Первый: Как удалить из памяти AcDb3dSolid* pTempSolid в следующем примере (ни delete pTempSolid, ни acdbFree(pTempSolid) не помогли):
void COctTree::TestMemory1()
{
  AcDb3dSolid* pSphere = new AcDb3dSolid;
  Acad::ErrorStatus es = pSphere->createSphere(50);
  if (es != Acad::eOk)
  {
    delete pSphere;
    return;
  }
  AddToCurGDb(pSphere);
  AcDb3dSolid* pMoveBox = new AcDb3dSolid;
  es = pMoveBox->createBox(10, 10, 10);
  if (es != Acad::eOk)
  {
    delete pMoveBox;
    return;
  }
  AddToCurGDb(pMoveBox);
  Adesk::Boolean bIsIntersection = Adesk::kFalse;
  AcDb3dSolid* pTempSolid = NULL;
  for (int i = 0; i < 10000; i++)
  {
    if( (es = pSphere->checkInterference(pMoveBox,Adesk::kTrue,bIsIntersection,pTempSolid)) != Acad::eOk )
      return ;
  // Work with pTempSolid
    if(pTempSolid)
    {
      pTempSolid->erase();
      acdbFree(pTempSolid);
//      delete pTempSolid;
    }
  }
Второй: Почему при работе следующего примера, а конкретнее, при отработке функции transformBy(), объём занимаемой памяти увеличивается? Как этого избежать?
void COctTree::TestMemory2()
{
  AcDb3dSolid* pMoveBox = new AcDb3dSolid;
  Acad::ErrorStatus es = pMoveBox->createBox(10, 10, 10);
  if (es != Acad::eOk)
  {
    delete pMoveBox;
    return;
  }
  double dbX = 0;
  double dbY = 0;
  double dbZ = 0;
  AcGeMatrix3d mat;
  for (int i = 0; i < 10000; i++)
  {
    dbX += 0.001;
    dbY += 0.001;
    dbZ += 0.001;
    mat(0,3) = dbX;
    mat(1,3) = dbY;
    mat(2,3) = dbZ;
    pMoveBox->transformBy(mat);
  }
  acutPrintf("\nComplete...");
  delete pMoveBox;
}
Заранее благодарен!

Re: Удаление примитивов и очистка памяти

> ALEXANDR
На оба вопроса - ответ один: "Так работает AutoCAD!":
1) Если объект (с указателем pObj) помещен в чертеж, то для его удаления следует выполнить:

pObj->erase();
pObj->close();

Причем память при этом не освободится - она освободится только после сохранения чертежа и повторной его загрузки!
2) Если же объект не в чертеже, то для его удаления следует выполнить:

delete pObj;

pObj->erase() и pObj->close() для такого объекта выполнять нельзя. Все остальное "от лукавого"!
В твоих примерах в чертеж ничего не добавляется - так что используй delete.
Это все в общем случае. В частном случае следующих примитивов AcDb3DSolid, AcDbBody, AcDbRegion можешь воспользоваться вызовом функции:

// После всех действий для освобождения
// используемой ACIS'ом памяти!
acdbAcisDeleteModelerBulletins();

Память должна освободиться.

Re: Удаление примитивов и очистка памяти

Благодарю, Александр! Попробую то, что Вы посоветовали. Если не получится, буду выкручиваться. Хотя Ваш ответ насчёт освобождения памяти после выгрузки чертежа меня несколько расстроил. Дело в том, что при построении октантных деревьев и последующей их визуализации используются сотни, если не тысячи (в зависимости от погрешности) солидов, и такая организация работы с памятью затруднить процесс построения. Но, в любом случае, дорогу осилит идущий, а за помощь ещё раз спасибо!

Re: Удаление примитивов и очистка памяти

Маленькое дополнение к ответу Александра Ривилиса. Перед вызовом acdbAcisDeleteModelerBulletins() должна быть хотя бы раз вызвана функция acdbAcisSetDeleteBulletins(Adesk::kTrue). Фунция acdbAcisDeleteModelerBulletins() для очистки памяти должна вызываться после каждого вызова оператора Delete либо функци transformBy() (возможно, и после некоторых других функций). Ниже представлены исправленные вышеизложенные примеры, в которых утечка памяти не наблюдается:
1.
void COctTree::TestMemory1()
{
  AcDb3dSolid* pSphere = new AcDb3dSolid;
  Acad::ErrorStatus es = pSphere->createSphere(50);
  if (es != Acad::eOk)
  {
    delete pSphere;
    return;
  }
  AcDb3dSolid* pMoveBox = new AcDb3dSolid;
  es = pMoveBox->createBox(10, 10, 10);
  if (es != Acad::eOk)
  {
    delete pMoveBox;
    return;
  }
  Adesk::Boolean bIsIntersection = Adesk::kFalse;
  AcDb3dSolid* pTempSolid = NULL;
  acdbAcisSetDeleteBulletins(Adesk::kTrue);//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  for (int i = 0; i < 10000; i++)
  {
    if( (es = pSphere->checkInterference(pMoveBox,Adesk::kTrue,bIsIntersection,pTempSolid)) != Acad::eOk )
      return ;
  // Work with pTempSolid
    if(pTempSolid)
    {
      delete pTempSolid;
      acdbAcisDeleteModelerBulletins();//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    }
  }
  acutPrintf("\nComplete...");
  delete pSphere;
  delete pMoveBox;
  acdbAcisDeleteModelerBulletins();//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}
2.
void COctTree::TestMemory2()
{
  AcDb3dSolid* pMoveBox = new AcDb3dSolid;
  Acad::ErrorStatus es = pMoveBox->createBox(10, 10, 10);
  if (es != Acad::eOk)
  {
    delete pMoveBox;
    return;
  }
  double dbX = 0;
  double dbY = 0;
  double dbZ = 0;
  AcGeMatrix3d mat;
  acdbAcisSetDeleteBulletins(Adesk::kTrue);//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  for (int i = 0; i < 10000; i++)
  {
    dbX += 0.001;
    dbY += 0.001;
    dbZ += 0.001;
    mat(0,3) = dbX;
    mat(1,3) = dbY;
    mat(2,3) = dbZ;
    pMoveBox->transformBy(mat);
    acdbAcisDeleteModelerBulletins();//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  }
  acutPrintf("\nComplete...");
  delete pMoveBox;
  acdbAcisDeleteModelerBulletins();//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}
Теперь буду думать, как удалить примитивы из графической БД автокада и из оперативки без выгрузки чертежа (Я признаю бесспорный авторитет Александра Ривилиса, но всё же надеюсь, что можно найти способ очистки памяти без использования выгрузки чертежа в файл и повторной загрузки - слишком нерационально). Если у кого какие будут варианты, буду признателен!

Re: Удаление примитивов и очистка памяти

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

Re: Удаление примитивов и очистка памяти

P.S.: А вот если работа идет только с памятью (без чертежа) - то все должно быть нормально.

Re: Удаление примитивов и очистка памяти

Попробуйте
http://dwg.ru/dnl/236
Буду рад, если поможет.
Могу опубликовать исходники.

Re: Удаление примитивов и очистка памяти

> Эдуард Яблонский
Очень интересно! Исходники или сама идея очень приветствуются!

Re: Удаление примитивов и очистка памяти

> Эдуард Яблонский
Немного потестировал Вашу программу в AutoCAD 2006:
1) Какие-то проблемы с прогрессбаром. После 35% он подвисает до окончания работы. Похоже Вы его слишком часто обновляете.
2) По поводу освобождения памяти есть тоже замечания. Тест такой:
а) в пустом чертеже рисую окружность - занято 43Мб памяти.
b) командой ARRAY создаю прямоугольный массив 300x300 окружностей - занято 107Мб памяти
c) удаляю все окружности - занято 61Мб памяти (как видите частично AutoCAD сам освободил память)
d) запускаю вашу очистку (она работает 20 секунд, находит и "удаляет" все 90000 примитивов) - занято 57Мб памяти (освободить удалось 4Мб - не густо)
e) сохраняю чертеж и загружаю (не выходя из AutoCAD) его по новой (на сохранение и загрузку ушло 10 секунд) - занято 44Мб памяти (освободилось еще 13Мб).
Возникает вопрос - в чем смысл Вашей очистки? Возможно мой тест не показательный, но тогда в какой ситуации он даст больший выигрыш, чем сохранение->загрузка чертежа?

Re: Удаление примитивов и очистка памяти

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

Re: Удаление примитивов и очистка памяти

Да, программа не полностью отлажена. С приёмом "сохранение" я и тягаться не стал бы, ибо вся идея "неуничтожения" объектов при их удалении применена для более простой реалиации Undo/Redo. Так как AutoCAD после сохранения "утрачивает" способность к Undo, то и объекты можно "реально" уничтожаются.
Идея была реалиована при разработке RCAD Steel (www.robobat.com) для удаления объектов "на лету" реконструировании чертежей.
Идея заключается в "подстановке" вместо удаляемых объектов, объектов минимального размера (равного AcDbObject/AcDbEntity). Таким обраом выигрышь заметен при удалении объектов большого объёма, например AcDbSolid.
С полосой прокрутки у меня были проблемы, вроде там ничего мудрёного не нужно делать, но он что-то "не красиво" работает. У меня не было времени и желания с ним рабираться, да и "прикрутил" я её туда так, для проформы.

Re: Удаление примитивов и очистка памяти

> Эдуард Яблонский
Спасибо за разъяснения! Честно говоря я еще вчера нашел в таблице импорта Вашего arx-файла AcDbObject::handOverTo(...) и предположил, что идет замена примитивов/объектов. Когда-то (не помню в какой из версий) я столкнулся с тем, что если этой функции на вход подается сложный примитив (имеющий подпримитивы: AcDb2dPolyline, AcDb3dPolyline, AcDbBlockReference с атрибутами и т.д.) который заменяется на простой, то AutoCAD через какое-то время разваливается (к AcDb3dSolid это не относится). Дальше с этим я экспериментировать не стал.

Re: Удаление примитивов и очистка памяти

А я люблю экспериментировать.
Однако, этот приём очень полезен. Например, я реализовывал таблицы в виде блоков примитивов и объектов текста. Эти таблицы активно обновлялись и могли менять свою структуру. Данный метод позволяет эффективно переиспользовать объекты, из которых состоят таблицы.