Место действия:
Небольшой класс на окраине проекта, обеспечивающий в числе прочего хранении ссылок на некоторые объекты:
TSomeClass=class
private
//....
hList:Tlist;
//....
Public
Constructor Create;
end;
implimentation
{TSomeClas}
TSomeClas.Create;
begin
inherited;
hList:=Tlist.Create;
end;
end.
Казалось бы... Приведенный код полностью рабочий и соответствует тому, что приведен в примерах в справке. Однако, не стоит верить всему, что пишут в справке!
Так, например, в справке есть очень интересное место, где описывает свойство Capacity.
Чем же оно привлекло мое внимание? Тем, что это свойство, если верить справке...
Specifies the allocated size of the array of pointers maintained by the TList objectТо есть "устанавливает размер выделенный пол массив указателей"! Уже интересно. Получается, что на самом деле массив, заключенный внутри класса TList на самом деле может быть или больше чем Count или такого же размера, так по крайней мере, мне подсказывает логика. Читаем хелп дальше:
Read Capacity to learn number of objects the list can hold without reallocating memory. Do not confuse Capacity with the Count property, which is the number of entries in the list that are in use. The value of Capacity is always greater than or equal to the value of Count. When Capacity is greater than Count, the unused memory can be reclaimed by setting Capacity to Count.
Ну, да. Так все и есть...
А теперь собственно вопрос: "Что произойдет, если объекту типа TList выполнить метод Add(), в тот момент, когда Count=Capacity?"
Ответ на этот вопрос есть конечно же в хелпе:
When an object is added to a list that is already filled to capacity, the Capacity property is automatically increased.
Ну, конечно, другого мы и не ожидали. Capacity будет увеличен. То есть должен быть увеличен. Соответственно, под "увеличенный" массив нужно будет больше памяти. И вот тут нас ждет небольшая неприятность. Как показали мои эксперименты Delphi может выдать ошибку типа EOutOfMemory, которая вообще говоря должна появляться при нехватке оперативной памяти, в ситуации когда происходит "увеличение" TList, ссылка на который хранится в другом TList, а на тот еще в одном.
То есть вот такая вот конструкция
Tlist(Tlist(Obj.hList[1])[33]).Add(some_pointer);
На самом деле объекты не наследники TList, а просто содержат свойство этого типа, но сути это не меняет. Так вот, если при выполнении комманды Add данном случае произойдет увеличение Capacity, то есть приличный (но не 100%!!) шанс получить вышеупомянутый эксепшен!!! Причем, самым "веселым" является тот факт, что эту ошибку мы получаем не всегда, мне повезло получить этот эксепшен за пару часов до отправки программы заказчику! До этого я почти полдня гонял программу на тестах! И ничего!
Решение, как и предполагалось, нашлось не самое красивое, зато надежное. Перед созданием объекта, который содержит поля типа TList, я вычисляю примерное количество указателей, которое мне потребуется хранить и передаю в конструктор. А Capacity выставляю в 1,5 раза больше. Потребление памяти, конечно, возрастает, но слишком сильно. Зато риски получить новую головную боль ввиде неуловимого и сложно воспроизводимого "борбага" становятся много меньше.
Удачной вам разработки!
Комментариев нет:
Отправить комментарий