Как "уронить" сборщик мусора?
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 12.11.04 05:57
Оценка: 3 (1)
Слышал, что можно как-то хитрым образом написать программу так, что сборщик мусора уйдет в глубокий даун разгребая мусор, короче будет работать неэффективно. Правда это или миф? Если у кого есть конкретные примеры таких программ — поделитесь, пожалуйста.


В свою очередь, попробовал испытать пронырливость сборщика мусора. Рассуждал так: чем больше ссылок между объектами — тем хуже для сборщика мусора. Если у нас есть N штук объектов, то максимально возможное количество ссылок между ними равно N^2 — это когда все объекты друг на друга ссылаются (включая ссылки на себя). То есть случай N^2 ссылок является самым сложным для сборщика мусора, так или не так?

Создаю N объектов, устанавливаю между ними N^2 связей, потом натравливаю на эту структуру сборщик мусора и измеряю время. Вот что наизмерял:
N =  1000, new  50   ms, GC  70   ms
N =  2000, new  230  ms, GC  261  ms
N =  3000, new  511  ms, GC  570  ms
N =  4000, new  912  ms, GC  1011 ms
N =  5000, new  1452 ms, GC  1773 ms
N =  6000, new  2083 ms, GC  2333 ms
N =  7000, new  5208 ms, GC  3995 ms
N =  8000, new  5138 ms, GC  4907 ms

В этой таблице, время "new" включает в себя время на создание N объектов и время на установление между ними N^2 связей, время "GC" включает в себя полную очистку памяти сборщиком мусора и подготовки ее к повторному использованию.

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

Я что-то не так померил или сборщик мусора действительно крут?


Исходный код программы:

MODULE TestGC;

IMPORT StdLog, Services;
    
PROCEDURE f(N: INTEGER);
TYPE Objects = POINTER TO ARRAY OF Object;
     Object  = POINTER TO RECORD ref: Objects END;
VAR obj: Objects; i,j,n: INTEGER; t: LONGINT;
BEGIN
  StdLog.String("N = "); StdLog.Int(N);

  (* Создание объектов *)
  t := Services.Ticks();
    NEW(obj, N);
    FOR i := 0 TO N-1 DO NEW(obj[i]) END;
    FOR i := 0 TO N-1 DO 
      NEW(obj[i].ref, N);
      FOR j := 0 TO N-1 DO obj[i].ref[j] := obj[j] END
    END;
  t := Services.Ticks() - t;
  StdLog.String(", new "); StdLog.Int(t); StdLog.String(" ms");

  (* Удаление объектов *)
  t := Services.Ticks();    
    obj := NIL;
    Services.Collect();
  t := Services.Ticks() - t;
  StdLog.String(", GC "); StdLog.Int(t); 
  StdLog.String(" ms");StdLog.Ln();
END f;

PROCEDURE Test* ();
VAR n: INTEGER;
BEGIN
  StdLog.Ln();
  FOR n := 1 TO 8 DO f(n*1000) END
END Test; 
    
END TestGC.

Размер одного объекта = 4*N + const, где const — это то что хранит в себе RTTI информацию.

Машина: процессор Celeron 1100 Coppermain 128Kb, память 448Mb PC100.
Re: Как "уронить" сборщик мусора?
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 12.11.04 06:00
Оценка:
P. S.
Забыл написать, что пользовался компилятором BlackBox 1.4
Re: Как "уронить" сборщик мусора?
От: Трурль  
Дата: 12.11.04 06:41
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

СГ> Рассуждал так: чем больше ссылок между объектами — тем хуже для сборщика мусора.

Это неверно.
Re: Как "уронить" сборщик мусора?
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 12.11.04 06:42
Оценка: +1
Здравствуйте, Сергей Губанов, Вы писали:

СГ>Я что-то не так померил или сборщик мусора действительно крут?


Перед удалением объектов вызови GC.Collect еще раз пока объекты не могут быть удалены. Для того чтобы они перешли в следующее поколение. И опубликуй плиз результаты. Они могут сильно отличаться
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re: Как "уронить" сборщик мусора?
От: alexeiz  
Дата: 12.11.04 08:00
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

СГ>Создаю N объектов, устанавливаю между ними N^2 связей, потом натравливаю на эту структуру сборщик мусора и измеряю время. Вот что наизмерял:

СГ>
СГ>N =  8000, new  5138 ms, GC  4907 ms
СГ>

5 секунд? Время сбора мусора 5 секунд? Что-же это за GC такой?
Re[2]: Как "уронить" сборщик мусора?
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 12.11.04 08:14
Оценка:
Здравствуйте, alexeiz, Вы писали:

A>5 секунд? Время сбора мусора 5 секунд? Что-же это за GC такой?


Все нормально. 8 тыс объектов, 16 млн связей — результат предсказуем.

Кстати, для .NET я вообще знаю способы заставить GC лечь намертво. Способы тривиальные.
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[3]: Как "уронить" сборщик мусора?
От: alexeiz  
Дата: 12.11.04 08:28
Оценка:
Здравствуйте, xvost, Вы писали:

X>Здравствуйте, alexeiz, Вы писали:


A>>5 секунд? Время сбора мусора 5 секунд? Что-же это за GC такой?


X>Все нормально. 8 тыс объектов, 16 млн связей — результат предсказуем.


X>Кстати, для .NET я вообще знаю способы заставить GC лечь намертво. Способы тривиальные.


Такой примерчик .NET GC соберет за >1ms. Он не будет подсчитывать ссылки в недоступных объектах. Кстати, "способы заставить .NET GC лечь намертво" — в студию.
Re[4]: Как "уронить" сборщик мусора?
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 12.11.04 08:44
Оценка:
Здравствуйте, alexeiz, Вы писали:

A>Такой примерчик .NET GC соберет за >1ms.

A>Он не будет подсчитывать ссылки в недоступных объектах.

Это так. Но если ты создашь 2 такие независимые струкуры, и попробуешь одну из них удалить — GC будет несколько секунд обегать имеющиеся объекты

A>Кстати, "способы заставить .NET GC лечь намертво" — в студию.


Лови.

class C
{
  public C() {}
  ~C() {System.Threading.Thread.Sleep(1000000);}

  public static void Main()
  {
    C c = new C();

    System.GC.Collect();
    System.GC.WaitForPendingFinalizers();
  }
}


Вызов System.GC.WaitForPendingFinalizers() вставлен только для того, чтобы в заставить отработать деструктор прямо сейчас. В реальной жизни этот вызов ненужен — рано или поздно деструктор запустится.

При этом основная неприятность всего этого — во время GC .NET полностью блокирует все потоки приложения. Т.е. имеем "мертвый вис".
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[5]: Как "уронить" сборщик мусора?
От: alexeiz  
Дата: 12.11.04 08:47
Оценка:
Здравствуйте, xvost, Вы писали:

X>Здравствуйте, alexeiz, Вы писали:


A>>Такой примерчик .NET GC соберет за >1ms.

A>>Он не будет подсчитывать ссылки в недоступных объектах.

X>Это так. Но если ты создашь 2 такие независимые струкуры, и попробуешь одну из них удалить — GC будет несколько секунд обегать имеющиеся объекты


Докажи.

A>>Кстати, "способы заставить .NET GC лечь намертво" — в студию.


X>Лови.


...

X>Вызов System.GC.WaitForPendingFinalizers() вставлен только для того, чтобы в заставить отработать деструктор прямо сейчас. В реальной жизни этот вызов ненужен — рано или поздно деструктор запустится.


X>При этом основная неприятность всего этого — во время GC .NET полностью блокирует все потоки приложения. Т.е. имеем "мертвый вис".


Такой "способ" я не приму. Я думал, что ты знаешь честный способ.
Re[5]: Как "уронить" сборщик мусора?
От: Воронков Василий Россия  
Дата: 12.11.04 08:52
Оценка: +1
Здравствуйте, xvost, Вы писали:

X>
X>class C
X>{
X>  public C() {}
X>  ~C() {System.Threading.Thread.Sleep(1000000);}

X>  public static void Main()
X>  {
X>    C c = new C();

X>    System.GC.Collect();
X>    System.GC.WaitForPendingFinalizers();
X>  }
X>}
X>


Это из области:

static void Main()
{
  for(;;) { }
}
Re[6]: Как "уронить" сборщик мусора?
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 12.11.04 08:56
Оценка:
Здравствуйте, Воронков Василий, Вы писали:

ВВ>
ВВ>static void Main()
ВВ>{
ВВ>  for(;;) { }
ВВ>}
ВВ>


НЕТ!

И вот почему:

Твой пример — блокируется только тред содержащий цикл. Т.е. например остальные потоки сервера спокойно продолжают обслуживать своих клиентов. В моем примере — нафиг блокируется ВЕСЬ сервер целиком.
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[5]: Как "уронить" сборщик мусора?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 12.11.04 09:03
Оценка:
Здравствуйте, xvost, Вы писали:

X>При этом основная неприятность всего этого — во время GC .NET полностью блокирует все потоки приложения. Т.е. имеем "мертвый вис".


А вот здесь ошибка — финалайзеры, в отличие от собственно сканирования графов и реструктуризации кучи, работают в отдельном потоке.
... << RSDN@Home 1.1.4 beta 3 rev. 230>>
AVK Blog
Re[6]: Как "уронить" сборщик мусора?
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 12.11.04 09:16
Оценка: :))
Здравствуйте, AndrewVK, Вы писали:

AVK>А вот здесь ошибка — финалайзеры, в отличие от собственно сканирования графов и реструктуризации кучи, работают в отдельном потоке.


Oops! Проверил — я был неправ.

Значит пока что я не знаю "честного" способа уронить GC
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[2]: А как измерить скорость сборки мусора?
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 12.11.04 09:20
Оценка:
Отвечаю сразу на 2 сообщения:

Здравствуйте, alexeiz, Вы писали:

СГ>>Создаю N объектов, устанавливаю между ними N^2 связей, потом натравливаю на эту структуру сборщик мусора и измеряю время. Вот что наизмерял:

СГ>>
СГ>>N =  8000, new  5138 ms, GC  4907 ms
СГ>>

A>5 секунд? Время сбора мусора 5 секунд? Что-же это за GC такой?

Здравствуйте, xvost, Вы писали:

X>Перед удалением объектов вызови GC.Collect еще раз пока объекты не могут быть удалены. Для того чтобы они перешли в следующее поколение. И опубликуй плиз результаты. Они могут сильно отличаться


Знаете, сейчас продолжил издевательства над BlackBox-совским GC и пришел к заключению, что вызов Services.Collect() для него ровным счетом ничего не означает (Даже не смотря на то, что выполняется много времени). Чем реально занимается этот Services.Collect(), ума не приложу , хотя в хелпе черным по белому написано: "Forces a garbage collection". Реальная чистка памяти, как я понял, возникает только тогда когда сам BlackBox этого захочет, а раз так, то у меня нет способа измерить реальную скорость сборки мусора в BlackBox. Вот такие пироги............ Я могу измерить только суммарную скорость операций "создание+удаление".


Вот, например, создание/удаление (в цикле 10 раз) N = 5000 объектов с N^2 ссылками друг на друга, на машине Athlon 1.47Ghz 256Mb DDR266 отнимает от 811 до 820 тиков.
MODULE TestGC;

IMPORT StdLog, Services;

PROCEDURE f(N: INTEGER);
TYPE Objects = POINTER TO ARRAY OF Object;
     Object  = POINTER TO RECORD ref: Objects END;
VAR obj: Objects; i,j: INTEGER;
BEGIN
  NEW(obj, N);
  FOR i := 0 TO N-1 DO NEW(obj[i]) END;
  FOR i := 0 TO N-1 DO 
    NEW(obj[i].ref, N);
    FOR j := 0 TO N-1 DO obj[i].ref[j] := obj[j] END
  END
END f;

PROCEDURE Test* ();
CONST N = 5000;
VAR n: INTEGER; t: LONGINT;
BEGIN
  t := Services.Ticks();
  FOR n := 1 TO 10 DO f(N) END;
  t := Services.Ticks() - t;
  StdLog.String("N ="); StdLog.Int(N);
  StdLog.String(", t ="); StdLog.Int(t DIV 10); StdLog.String(" ms");
  StdLog.Ln();
END Test; 
    
END TestGC.


Log:

N = 5000, t = 811 ms


Но сколько именно из этой 0.8 секунды тратится на создание такой сложной структуры в памяти, а сколько тратится на ее уничтожение — я не знаю. В сумме 0.8 секунды и точка.
Re[7]: Как "уронить" сборщик мусора?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 12.11.04 09:35
Оценка:
Здравствуйте, xvost, Вы писали:

X>Oops! Проверил — я был неправ.


X>Значит пока что я не знаю "честного" способа уронить GC


Что значит честный? Вот такой код собственно в свое время через продолжительное время убивал януса:
public sealed class SomeSingleton
{
    private SomeSingleton() {}
    
    public readonly SomeSingleton Instance = new SomeSingleton();
    
    public event EventHandler SomeEvent;
}

...

private void RefreshForums
{
    _forums.Clear();
    foreach (ForumData fd in GetFromDb())
        _forums.Add(new Forum(fd));
}

...

public class Forum
{
    public Forum(ForumData fd)
    {
        //...
        SomeSingleton.SomeEvent += new EventHandler(SomeEventFired);
    }
    
    //...
}
... << RSDN@Home 1.1.4 beta 3 rev. 230>>
AVK Blog
Re[2]: Как "уронить" сборщик мусора?
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 12.11.04 12:29
Оценка:
Здравствуйте, Трурль, Вы писали:

Т>Здравствуйте, Сергей Губанов, Вы писали:


СГ>> Рассуждал так: чем больше ссылок между объектами — тем хуже для сборщика мусора.

Т>Это неверно.

А как верно?
Re[5]: Как "уронить" сборщик мусора?
От: VladD2 Российская Империя www.nemerle.org
Дата: 12.11.04 17:43
Оценка:
Здравствуйте, xvost, Вы писали:

X>Лови.


X>
X>class C
X>{
X>  public C() {}
X>  ~C() {System.Threading.Thread.Sleep(1000000);}

X>  public static void Main()
X>  {
X>    C c = new C();

X>    System.GC.Collect();
X>    System.GC.WaitForPendingFinalizers();
X>  }
X>}
X>


Ты его хоть запускал?

X>Вызов System.GC.WaitForPendingFinalizers() вставлен только для того, чтобы в заставить отработать деструктор прямо сейчас. В реальной жизни этот вызов ненужен — рано или поздно деструктор запустится.


Трепачь находка для шпионов. Если ЖЦ видет что финалайзер занят более чем на определенное время, то плюет на финализацию и продолжает работу.

X>При этом основная неприятность всего этого — во время GC .NET полностью блокирует все потоки приложения. Т.е. имеем "мертвый вис".


Кончай нести фигню и пойди почитай Рихтера.
... << RSDN@Home 1.1.4 beta 3 rev. 207>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Как "уронить" сборщик мусора?
От: VladD2 Российская Империя www.nemerle.org
Дата: 12.11.04 17:43
Оценка:
Здравствуйте, Воронков Василий, Вы писали:

ВВ>Это из области:


ВВ>
ВВ>static void Main()
ВВ>{
ВВ>  for(;;) { }
ВВ>}
ВВ>


Не это из другой области. Запусти пример и увидишь что проблем не возникнет. Так что это из области трепачей.

Ну, а завалить ЖЦ действительно не трудно. Достаточно просто не освобождать ссылки. Что с успехом продемонстрировал Синклер. Как говорится:
ArrayList _garbage = new ArrayList(); // складывать мусор тут :)
... << RSDN@Home 1.1.4 beta 3 rev. 207>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: Как "уронить" сборщик мусора?
От: VladD2 Российская Империя www.nemerle.org
Дата: 12.11.04 17:43
Оценка:
Здравствуйте, xvost, Вы писали:

X>Твой пример — блокируется только тред содержащий цикл. Т.е. например остальные потоки сервера спокойно продолжают обслуживать своих клиентов. В моем примере — нафиг блокируется ВЕСЬ сервер целиком.


1. Не ври.
2. Подними приоритет у потока и погляди как там остальные потоки спокойно...
... << RSDN@Home 1.1.4 beta 3 rev. 207>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[8]: Как "уронить" сборщик мусора?
От: VladD2 Российская Империя www.nemerle.org
Дата: 12.11.04 17:43
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Что значит честный? Вот такой код собственно в свое время через продолжительное время убивал януса:


Это не проблемы не сборщика мусора, а проблема реализации событий. Тормоза там наступаеют не при ЖЦ, а именно при вызове событий или при привязке/отвязке.
... << RSDN@Home 1.1.4 beta 3 rev. 207>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.