Delphi 2009 - Может ли свойство интерфейса вызвать утечку памяти? - PullRequest
4 голосов
/ 24 июня 2009

Я унаследовал приложение Intraweb с текстовым файлом утечек памяти объемом 2 МБ, как сообщает FastMM4. У меня это до 115 экземпляров одного класса с утечкой 52 байта.

Краткое описание плохого актера:

TCwcBasicAdapter = class(TCwcCustomAdapter)  
  protected  
    FNavTitleField: TField;  
    function GetAdapterNav(aDataSet: TDataSet): ICwcCDSAdapterNav; override;  
  public  
    constructor Create(aDataSource: TDataSource; aKeyField, aNavTitleField: TField; aMultiple: boolean);  
  end;  

и интерфейс:

  ICwcCDSAdapterNav = interface(IInterface)  

Я лаю не на том дереве, так как свойство считается подсчитанным? Существуют ли обстоятельства, при которых свойство интерфейса может предотвратить уничтожение класса?

Вот реализация описанного выше метода:

function TCwcBasicAdapter.GetAdapterNav(aDataSet: TDataSet): ICwcCDSAdapterNav;
var
  AdapterNav: TCwcCDSAdapterNavBase;
begin
  result := nil;
  if Assigned(aDataSet) then begin
    AdapterNav := TCwcCDSAdapterNavBasic.Create(aDataSet, FKeyField.Index, FNavTitleField.Index);
    try
      AdapterNav.GetInterface(ICwcCDSAdapterNav, result);
    except
      FreeAndNil(AdapterNav);
      raise;
    end;
  end;
end;

с классом, объявленным как:

TCwcCDSAdapterNavBase = class(TInterfacedObject, ICwcCDSAdapterNav)

Ответы [ 3 ]

4 голосов
/ 24 июня 2009

У вас есть несколько хороших ответов о том, как работает FastMM. Но что касается вашего реального вопроса, да, интерфейсные объекты могут протекать двумя разными способами.

  1. Интерфейсы подсчитываются только в том случае, если объекты, которым они принадлежат, реализовали подсчет ссылок в своих методах _AddRef и _Release. Некоторые объекты этого не делают.
  2. Если у вас есть циклические ссылки на интерфейс (интерфейс 1 ссылается на интерфейс 2, который ссылается на интерфейс 1), то счетчик ссылок никогда не упадет до 0 без каких-либо специальных приемов с вашей стороны. Если это ваша проблема, я отсылаю вас к недавнему блогу Андреаса Хаусладена на эту тему.
4 голосов
/ 24 июня 2009

FastMM должен дать вам то, что просочилось и где оно было создано.
Это помогло бы сузить круг до настоящего виновника: кто что пропускает?

Я не уверен, что на самом деле ваш вопрос?
Ваш код неполный или не тот, о котором идет речь: у вашего класса нет ни свойства Interface, ни частного поля Interface, а только метод, который возвращает интерфейс, который безвреден.

Редактировать : Не видя код вашего Объекта, реализующего ICwcCDSAdapterNav, мы не можем определить, действительно ли он подсчитан.
Если вы не сходите с TInterfacedObject , скорее всего, , что это не подсчитано, и , что вы не можете полагаться на это автоматически освобождающееся ...

Возможно, вы захотите взглянуть на этот CodeRage 2 сеанс : Борьба с утечками памяти для чайников . В основном показано, как использовать FastMM для предотвращения / обнаружения утечек памяти в Delphi. Был для D2007, но все еще актуален для других версий.

2 голосов
/ 24 июня 2009

Если вы пропускаете 115 экземпляров этого класса, то этот класс пропускается. Память, занятая этим классом, а не память, занимаемая вещами, на которые он ссылается, протекает. Где-то у вас есть 115 экземпляров TCwcBasicAdapter, которые вы не освобождаете.

Кроме того, свойства не хранят данные, независимо от того, являются ли они интерфейсами или какими-либо другими типами. Только поля занимают память (наряду с некоторым скрытым пространством, которое компилятор выделяет от имени класса).

Итак, да, вы лаете не на то дерево. Ваша утечка памяти где-то еще. Когда FastMM сообщает вам, что у вас есть утечка памяти, он также не сообщает вам, где был выделен каждый экземпляр утечки. У него есть такая возможность; вам может потребоваться настроить некоторые символы условной компиляции, чтобы включить эту функцию.

Конечно, это не только экземпляры этого класса, которые протекают. FastMM также должен сообщать о некоторых других утечках, таких как экземпляры класса или классы, реализующие интерфейс.


Основываясь на добавленной вами функции, я начал подозревать, что на самом деле TCwcCDSAdapterNavBase протекает, и это может быть из-за нетипичного способа, которым вы его используете. Работает ли обработчик исключений в GetAdapterNav? Я сомневаюсь; TObject.GetInterface никогда явно не вызывает исключение. Если объект не поддерживает интерфейс, он возвращает False. Все, что может обработать исключение, это такие вещи, как нарушение прав доступа и незаконные операции, которые вам действительно не следует ловить в любом случае.

Вы можете реализовать эту функцию более прямо так:

if Assigned(FDataSet) then
  Result := TCwcCDSAdapterNavBase.Create(...);
...