Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Общие вопросы по .NET и C# > Удаление объектов


Автор: Freeman 23.2.2005, 19:40
Начал изучать C# и решил написать простенький список, но столкнулся с проблемой удаления объектов. Я знаю что в С# автоматическое удаление, но что если надо удалить элемент сразу, не дожидаясь пока это сделает сборщик мусора.
Если можно ответ, пояснить примером.

Автор: Domestic Cat 23.2.2005, 20:06
Объект подлежит удалению тогда, когда на него нет ссылок - например, ссылка на него была в локальной переменной, или ты ее занулил. Тогда можно вызвать сборщик мусора, который и уберет ненужный объекt:

Код

myObject = null;
GC.Collect();


С другой стороны, в постоянном вызове сборщикa ничего хорошего нет .

Автор: Gazon 23.2.2005, 23:47
Цитата
С другой стороны, в постоянном вызове сборщикa ничего хорошего нет .

И с ним многие советуют не связываться ни в коем случае.

Автор: Tomcat 24.2.2005, 11:03
Был у меня однажды случай. Прога была, в принципе, простенькая. Суть заключалась в том, что она собирала кучу информации с файлов, а потом одним махом заносила ее в БД на SQL Server. По началу все было не плохо... Но затем пошли файлы, у которых эта куча была слишком большой, да такой, что вылетал Out of memory. Когда посмотрели загруженность памяти, поняли, что все вполне понятно, выделяемая под процесс проги область разрасталась до 2-3 Гб, да плюс еще и SQL Server ел не мало.
Ну что же, и все из-за не удаленных промежуточных объектов, появляющихся при обработке файлов. Так вот, код типа:
Код

myObject = null;
GC.Collect();

не помогал вовсе. Беглый просмотр документации привел к выводу, что необходимо GC.Collect() вызывать два раза. Это тоже не помогало.
Нам тогда повезло, появились другие исходные данные, которые были намного удобней для обработки, на том и выехали.

Автор: Дрон 24.2.2005, 15:36
Посмотри метод Dispose для объёктов. Может поможет smile

Автор: Freeman 24.2.2005, 22:34
К сожалению ни один вариант не решил проблемы, но может я что-то не так делаю, вот код
Код

class List
{
 private class Node : IDisposable
 {
  public int val;
  public Node next;

  public Node() { }
  public Node(int v)
  {
   val = v;
  }
  public Node(int v, Node n)
  {
   val = v;
   next = n;
  }

  public void Dispose()
  {
   
  }
 }

 private int count;
 private Node head;

 public void Insert(int v)
 {
  Node pn = head, last = null, pnew;
 
  for (; pn != null  &&  pn.val <= v; last = pn, pn = pn.next);

  pnew = new Node(v);
  if (last == null)
  {
   pnew.next = head;
   head = pnew;
  }
  else
  {
   pnew.next = pn;
   last.next = pnew;
  }

  count++;
 }

 public void Show()
 {
  Node pn = head;
  int i = 0;

  Console.WriteLine("List...");
  while (pn != null)
  {
   Console.WriteLine("[{0}]: {1}", i, pn.val);
   i++;
   pn = pn.next;
  }
 }

 public bool Delete(int v)
 {
  Node pn = head, last = null;

  for (; pn != null && pn.val != v; last = pn, pn = pn.next);

  if (pn != null)
  {
   if (last != null)
   {
    last.next = pn.next;
   }
   else
   {
    head = pn.next;
   }
  }
  else
  {
   return false;
  }

  //так не помогает pn = null;
  //и так не помогаетGC.Collect();
  pn.Dispose(); //и так тоже
  count--;
  return true;
 }
}


а это тест

Код

class Program
{
 static void Main(string[] args)
 {
  List d = new List();
  int NN = 1000000;

  for (int i = NN-1; i > -1; i--)
  {
   d.Insert(i);
  }

  for (int i = 0; i < NN; i++)
  {
   d.Delete(i);
  }
 }
}


Автор: Domestic Cat 24.2.2005, 23:28
Код

private class Node : IDisposable
  {
   public int val;
       public Node next;
//....
         public void Dispose()
       {
             next = null; // на всякий случай 
     }
  }



Код

public bool Delete(int v)
  {
//...
    pn.Dispose(); 
    pn = null;
       count--;
         return true;
  }


1. Как ты определил что "не помогает"?
2. На кой из-за одного объекта в пару десяков байт вызывать сборщик?
Вроде никаких ошбок не вижу, хотя названия переменным можно было бы и поприличее дать.

Автор: Plamenk 25.2.2005, 12:49
Вообще использование интерфейса IDisposable, никак не связано с удалением объекта!
Если класс реализует данный интерфейс, то подразумевается что он использует неуправляемые ресурсы и в функции Dispose просто происходит их очистка, так как сборщик мусора сам не сбособен освобождать такие ресурсы!

Что касается однозначного удаления объекта, то необходимо ИХМО как-то использовать GC и удалять все ссылки на объект!

Автор: Freeman 25.2.2005, 19:54
to Domestic Cat
Цитата
Как ты определил что "не помогает"?

Очень просто запустил тест и посмотрел в диспетчере задач, там есть объем занятой памяти, при удалении память должна освобождаться а она не освобождается

Цитата
На кой из-за одного объекта в пару десяков байт вызывать сборщик?

Так на всякий случай smile)

to Plamenk
Цитата
удалять все ссылки на объект!

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

Код

public void Dispose()
{
     next = null;
}

они тоже обнуляются, так что не остаются никаких действующий ссылок на удаленный элемент.

Автор: Domestic Cat 25.2.2005, 20:07
Цитата
Очень просто запустил тест и посмотрел в диспетчере задач, там есть объем занятой памяти, при удалении память должна освобождаться а она не освобождается


Дык посмотрi нa свой код - у тебя удаление идет в цикле. Расходуешь памяти ты немного, потом все объекты удаляешь. Вполне возможно что сборщик запускается при удалении, но диспетчер не успевает отобразить изменениe памяти.

[Offtop]
Кстати, вопрос : есть ли что нибудь наподобиe флагa -verbose:gc , как в Java?
[/Offtop]

Автор: Freeman 25.2.2005, 22:13
to Domestic Cat
Проблема не в диспетчере, потому что точно такой же код написанный на обычном C++, работает как надо и диспетчер отображает освобождение памяти в реальном времени.

Автор: Дрон 26.2.2005, 01:08
Цитата(Domestic @ 25.2.2005, 20:07)
Вполне возможно что сборщик запускается при удалении, но диспетчер не успевает отобразить изменениe памяти.

Сборщик мусора запускается тогда, когда сочтёт нужным.


Цитата(Domestic @ 25.2.2005, 20:07)
Кстати, вопрос : есть ли что нибудь наподобиe флагa -verbose:gc , как в Java?

А что он делает?

Автор: Domestic Cat 26.2.2005, 03:39
Цитата
Сборщик мусора запускается тогда, когда сочтёт нужным.


Любой сборщик запускается когда захочет smile

Цитата
А что он делает?


Вывводит инфу - сколько с какой области хипа собрано. В Java есть поколения, только не 1, 2, 3 как в .НЕТ, а молодое и старое.
Что-то вроде
Цитата

.....
[GC 622K->110K(1984K), 0.0010317 secs]
[GC 622K->110K(1984K), 0.0010261 secs]
[GC 622K->110K(1984K), 0.0010359 secs]
[GC 622K->110K(1984K), 0.0010300 secs]
[GC 622K->110K(1984K), 0.0011647 secs]
[GC 622K->110K(1984K), 0.0010147 secs]
[GC 622K->110K(1984K), 0.0011021 secs]
[GC 622K->110K(1984K), 0.0010283 secs]
[GC 622K->110K(1984K), 0.0010306 secs]
[GC 622K->110K(1984K), 0.0010174 secs]
[GC 622K->110K(1984K), 0.0010345 secs]
[GC 622K->110K(1984K), 0.0018662 secs]
[GC 622K->110K(1984K), 0.0012367 secs]
.....

Автор: Freeman 26.2.2005, 18:36
Я вообще думал что это простой вопрос, неужели никто не делал каких-нибудь динамических структур на C#, хоть малюсенький дин. стек smile

Автор: Дрон 26.2.2005, 19:57
Цитата(Freeman @ 26.2.2005, 18:36)
Я вообще думал что это простой вопрос, неужели никто не делал каких-нибудь динамических структур на C#, хоть малюсенький дин. стек smile

А в чём проблема? smile

Domestic Cat
Нет. По крайней мере, я такого не видел.

Автор: Domestic Cat 27.2.2005, 07:26
Нашел я как делать.
1. Запускаем perfmon.exe из Выполнить.
2. Кликаем правой кнопкой на правой панели и выбираем "Добавить счетчики..."
3. Выбираем объект Память CLR .NET и Выбрать все счетчики
4. Нажимаем Добавить и Закрыть
5. Запускаем ченить НЕТовское и смотрим, лучше под видом "Просмотр отчета".

Автор: Freeman 27.2.2005, 08:57
to Дрон
Проблема в том, что удаление не работает так как должно.

Автор: Domestic Cat 27.2.2005, 09:18
Цитата(Freeman @ 26.2.2005, 23:57)
Проблема в том, что удаление не работает так как должно.


Все на самом деле работает. Если не веришь - вот пример:
Код

class Class1
{
 static ArrayList list = new ArrayList();
 /// <summary>
 /// The main entry point for the application.
 /// </summary>
 
 [STAThread]
 static void Main(string[] args)
 {
  long totalMemory = GC.GetTotalMemory(true);
  Console.WriteLine("Was: " + totalMemory);
  FillList();
  totalMemory = GC.GetTotalMemory(true);
  Console.WriteLine("Before removing: " + totalMemory);
  RemoveAllFromList();
  totalMemory = GC.GetTotalMemory(true);
  Console.WriteLine("After removing: " + totalMemory);
 }

 private static void FillList()
 {
  for (int i = 0; i < 1000; i++)
   list.Add(new Object());
 }

 private static void RemoveAllFromList()
 {
  for (int i = 1000; i > 0; i--)
   list.RemoveAt(0);
 }
}


Результат:
Цитата
Was: 45116
Before removing: 64200
After removing: 52200


64200 - 52200 = 12000 байт
Всего объектов 1000 - значит, по 12 байт на каждый Объект, как и должно быть. То есть, все объекты после метода RemoveAllFromList были вычищены.

Автор: Freeman 27.2.2005, 10:44
to Domestic Cat
Я не спорю с ArrayList все работает, но я не думаю что в реальном приложении Вы будете пользоваться таким списком. По-крайней мере из-за двух причин :
1. Слишком медленный(в части удаления), запустите Ваш пример не на 1000, а на 100000 и больше.
2. Расход памяти одинаковый для любых объектов, и равен <размер списка>*sizeof(Object), что наводит на мысль что в ArrayList хранятся не сами объекты, а лишь ссылки на них, причем ссылки типа Object, из-за этого возникает дополнительная трудоемкость, более того если со ссылочными типами происходит только преобразование типа, то со структурными типами происходит упаковка объекта, что также не улучшает эффективность данной структуры.
3. Если можно, не могли бы Вы объяснить следующую вешь.
Was: 211380
Before: 1938880
After: 738868
почему разница между After - Was, такая большая?

А вот если Вы поставите в нужные места в моем незамысловатом коде следующий код
Код

memory = GC.GetTotalMemory(true);
Console.WriteLine(".....: {0}", memory);

то получатся следующие результаты
Was: 216656
Before: 1819012
After: 219012

Тестирование в обоих случаях проводилось на 100000.

Автор: Domestic Cat 27.2.2005, 11:09
Цитата(Freeman @ 27.2.2005, 01:44)
но я не думаю что в реальном приложении Вы будете пользоваться таким списком.


Почему не буду, буду. Свое-то точно сочинять не собираюсь.

Цитата(Freeman @ 27.2.2005, 01:44)
Слишком медленный(в части удаления), запустите Ваш пример не на 1000, а на 100000 и больше.

ArrayList по идее должен быть реализован в виде массива. Удаление действительно долго идет, но зато доступ быстрее.

Цитата(Freeman @ 27.2.2005, 01:44)
Расход памяти одинаковый для любых объектов, и равен <размер списка>*sizeof(Object), что наводит на мысль что в ArrayList хранятся не сами объекты, а лишь ссылки на них,

Само собой

Цитата(Freeman @ 27.2.2005, 01:44)
причем ссылки типа Object

Да, Объект, иначе он бы вообще ничего не смог хранить. Не устраивает - есть generics.

Цитата(Freeman @ 27.2.2005, 01:44)
ссылочными типами происходит только преобразование типа, то со структурными типами происходит упаковка объекта, что также не улучшает эффективность данной структуры.

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

Цитата(Freeman @ 27.2.2005, 01:44)
почему разница между After - Was, такая большая?

Помимо твоих объектов, нахипе создаются и другие, которые ты "не видишь". Например простейшая операция
Код

string с = "Хелло " + "Ворлд!";

создает как минимум 3 объекта.
Помимо этого, getTotalMemory не всегда выдает точное значение.


Цитата(Freeman @ 27.2.2005, 01:44)
то получатся следующие результаты
Was: 216656
Before: 1819012
After: 219012


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

Автор: Freeman 27.2.2005, 14:23
Объекты удаляются лишь виртуально, физическая память не освобождается. Это аналогично работе "Корзины" в Винде, вроде бы файлы удаляются, но место на диске все равно жрут, по не очистишь smile

Цитата
Помимо твоих объектов, нахипе создаются и другие, которые ты "не видишь".

И как я понял они не удаляются.

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

Автор: Дрон 27.2.2005, 14:59
Freeman
Чегой-то я не понимаю, в чём же всё-таки у тебя проблема?
Такова уж особенность систем с автоматической сборкой мусора smile

Хочется самостоятельно управлять памятью? Так напиши на Си какую-нибудь библиотеку, сделай там функции аналогичные malloc и free, подключи в .NET и используй их (хотя это бредовая и бесполезная идея) smile

Автор: Freeman 1.3.2005, 18:19
Проблема была в том что удаление работало на половину, из кучи объекты удалялись, а из физической памяти нет. Но в конце концов я понял в чем была причина. Все дело было в сборщике мусора, он не работает в Visual Studio 2005 Beta 1, по крайней мере так как должен. Но у меня имеется также и 2003 там все работает прекрасно.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)