Возможный вынудить Delphi threadvar Память, которая будет Освобождена?

@Paul Lynch - отличный ответ, но он изменит соотношение изображения. если вы не хотите менять соотношение изображения и хотите, чтобы новое изображение соответствовало новому размеру, попробуйте это.

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {

// calculate a new size which ratio is same to original image
CGFloat ratioW = image.size.width / newSize.width;
CGFloat ratioH = image.size.height / newSize.height;

CGFloat ratio = image.size.width / image.size.height;

CGSize showSize = CGSizeZero;
if (ratioW > 1 && ratioH > 1) { 

    if (ratioW > ratioH) { 
        showSize.width = newSize.width;
        showSize.height = showSize.width / ratio;
    } else {
        showSize.height = newSize.height;
        showSize.width = showSize.height * ratio;
    }

} else if (ratioW > 1) {

    showSize.width = showSize.width;
    showSize.height = showSize.width / ratio;

} else if (ratioH > 1) {

    showSize.height = showSize.height;
    showSize.width = showSize.height * ratio;

}

//UIGraphicsBeginImageContext(newSize);
// In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
// Pass 1.0 to force exact pixel size.
UIGraphicsBeginImageContextWithOptions(showSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, showSize.width, showSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;}
12
задан Mark Wilkins 2 July 2009 в 14:01
поделиться

3 ответа

Рискуя написать слишком много кода, вот возможное (плохое) решение моего собственного вопроса. Используя тот факт, что локальная память потока хранится в одном блоке для переменных threadvar (как указал г-н Кеннеди - спасибо), этот код сохраняет выделенные указатели в TList, а затем освобождает их при отсоединении процесса. Я написал это в основном, чтобы посмотреть, будет ли это работать. Я, вероятно, не стал бы использовать это в производственном коде, потому что он делает предположения о среде выполнения Delphi, которая может измениться с разными версиями, и, вполне возможно, пропускает проблемы даже с версией, которую я использую (Delphi 7 и 2007).

Эта реализация действительно делает umdh счастлив, он не думает, что утечек памяти больше. Однако, если я запускаю тест в цикле (загрузка, вызов точки входа в другом потоке, выгрузка), использование памяти, как видно в Process Explorer, по-прежнему растет тревожно быстро. Фактически, я создал полностью пустую DLL, содержащую только пустой DllMain (который не был вызван, поскольку я не назначал ему глобальный указатель DllMain Delphi ... Сам Дели предоставляет реальную точку входа DllMain). Простой цикл загрузки / выгрузки DLL все равно пропускал 4К за итерацию. Таким образом, все еще может быть что-то еще, что Delphi DLL должна включать (основной смысл исходного вопроса). Но я не знаю, что это такое. DLL, написанная на C, не ведет себя подобным образом.

Наш код (сервер) может вызывать библиотеки DLL, написанные клиентами, для расширения функциональности. Обычно мы выгружаем DLL после того, как на нее больше нет ссылок. Я думаю, что моим решением проблемы будет добавление возможности оставлять DLL загруженной «постоянно» в памяти. Если клиенты используют Delphi для написания своей DLL, им нужно будет включить эту опцию (или, может быть, мы сможем определить, что это Delphi DLL при загрузке ... необходимо проверить это). Тем не менее, это было интересное упражнение.

library Sample;

uses
  SysUtils,
  Windows,
  Classes,
  HTTPApp,
  SyncObjs;

{$E dll}

var
   gListSync : TCriticalSection;
   gTLSList  : TList;


threadvar
   threadint : integer;


// remove all entries from the TLS storage list
procedure RemoveAndFreeTLS();
var
   i : integer;
begin
   // Only call this at process detach. Those calls are serialized
   // so don't get the critical section.
   if assigned( gTLSList ) then
      for i := 0 to gTLSList.Count - 1 do
         // Is this actually safe in DllMain process detach?  From reading the MSDN
         // docs, it appears that the only safe statement in DllMain is "return;"
         LocalFree( Cardinal( gTLSList.Items[i] ));

end;


// Remove this thread's entry
procedure RemoveThreadTLSEntry();
var
   p : pointer;
begin
   // Find the entry for this thread and remove it.
   gListSync.enter;
   try
      if ( SysInit.TlsIndex <> -1 ) and ( assigned( gTLSList )) then
         begin
            p := TlsGetValue( SysInit.TlsIndex );

            // if this thread didn't actually make a call into the DLL and use a threadvar
            // then there would be no memory for it
            if p <> nil then
               gTLSList.Remove( p );
         end;

   finally
      gListSync.leave;
   end;
end;


// Add current thread's TLS pointer to the global storage list if it is not already
// stored in it.
procedure AddThreadTLSEntry();
var
   p : pointer;
begin
   gListSync.enter;
   try
      // Need to create the list if first call
      if not assigned( gTLSList ) then
         gTLSList := TList.Create;

      if SysInit.TlsIndex <> -1 then
         begin
            p := TlsGetValue( SysInit.TlsIndex );

            if p <> nil then
               begin
               // if it is not stored, add it
               if gTLSList.IndexOf( p ) = -1 then
                  gTLSList.Add( p );
               end;
         end;

   finally
      gListSync.leave;
   end;
end;



// Some entrypoint that uses threadvar (directly or indirectly)
function MyExportedFunc(): LongWord; stdcall;
begin
   threadint := 123;

   // Make sure this thread's TLS pointer is stored in our global list so
   // we can free it at process detach.  Do this AFTER using the threadvar.
   // Delphi seems to allocate the memory on demand.
   AddThreadTLSEntry;
   Result := 0;
end;



procedure DllMain(reason: integer) ;
begin
   case reason of
     DLL_PROCESS_DETACH:
     begin
        // NOTE - if this is being called due to process termination, then it should
        // just return and do nothing.  Very dangerous (and against MSDN recommendations)
        // otherwise.  However, Delphi does not provide that information (the 3rd param of
        // the real DlLMain entrypoint).  In my test, though, I know this is only called
        // as a result of the DLL being unloaded via FreeLibrary
        RemoveAndFreeTLS();
        gListSync.Free;
        if assigned( gTLSList ) then
           gTLSList.Free;
     end;

     DLL_THREAD_DETACH:
        begin
        // on a thread detach, Delphi will clean up its own TLS, so we just
        // need to remove it from the list (otherwise we would get a double free
        // on process detach)
        RemoveThreadTLSEntry();
        end;

   end;
end;




exports
   DllMain,
   MyExportedFunc;


// Initialization
begin
   IsMultiThread := TRUE;

   // Make sure Delphi calls my DllMain
   DllProc := @DllMain;

   // sync object for managing TLS pointers.  Is it safe to create a critical section?
   // This init code is effectively DllMain's DLL_PROCESS_ATTACH
   gListSync := TCriticalSection.Create;
end.
3
ответ дан 2 December 2019 в 21:45
поделиться

Как вы уже определили, локальное хранилище потока будет освобождено для каждого потока, который отключается от DLL. Это происходит в System._StartLib , когда Причина - DLL_Thread_Detach . Однако, чтобы это произошло, поток должен завершиться. Уведомления об отсоединении потока появляются при завершении потока, а не при выгрузке библиотеки DLL. (Если бы все было наоборот, ОС пришлось бы где-нибудь прервать поток, чтобы она могла вставить вызов DllMain от имени потока. Это было бы катастрофой.)

DLL ] должен получать уведомления об отсоединении потоков. Фактически, это модель, предложенная Microsoft в описании того, как использовать локальное хранилище потока с библиотеками DLL .

Единственный способ освободить локальное хранилище потока - это вызвать TlsFree из контекста потока, хранилище которого вы хотите освободить. Насколько я могу судить, Delphi хранит все свои потоки в одном индексе TLS, заданном переменной TlsIndex в SysInit.pas . Вы можете использовать это значение для вызова TlsFree , когда захотите, но лучше быть уверенным, что больше не будет кода, выполняемого DLL в текущем потоке.

Поскольку вы также хотите, чтобы Чтобы освободить память, используемую для хранения всех переменных потоков, вам нужно вызвать TlsGetValue , чтобы получить адрес буфера, который выделяет Delphi. Вызовите LocalFree для этого указателя.

Это будет (непроверенный) код Delphi для освобождения локального хранилища потока.

var
  TlsBuffer: Pointer;
begin
  TlsBuffer := TlsGetValue(SysInit.TlsIndex);
  LocalFree(HLocal(TlsBuffer));
  TlsFree(SysInit.TlsIndex);
end;

Если вам нужно сделать это из хост-приложения, а не из библиотеки DLL, вам потребуется экспортировать функцию, которая возвращает значение TlsIndex библиотеки DLL. Таким образом, основная программа может освободить само хранилище после того, как DLL исчезнет (таким образом, гарантируя, что дальнейший код DLL не будет выполняться в данном потоке).

6
ответ дан 2 December 2019 в 21:45
поделиться

You might be needing a cleanup

git gc --prune=now

or you might be needing a prune

git remote prune public

prune

Deletes all stale tracking branches under . These stale branches have already been removed from the remote repository referenced by , but are still locally available in "remotes/".

With --dry-run option, report what branches will be pruned, but do no actually prune them.

However, it appears these should have been cleaned up earlier with

git remote rm public 

rm

Remove the remote named . All remote tracking branches and configuration settings for the remote удалены.

Так что, возможно, вы вручную отредактировали свой файл конфигурации, и этого не произошло, или у вас проблемы с правами доступа.

Может быть, запустите это еще раз и посмотрите, что произойдет.

Advice Context

Если вы посмотрите журналы ревизий , вы заметите, что я предложил более «правильные» методы, которые по какой-то причине не хотели работать в их репозитории.

Я подозревал, что OP сделал что-то, что привело к тому, что их дерево оказалось в несогласованном состоянии, из-за чего оно вело себя немного странно, и git gc требовалось исправить оставленный хлам.

Обычно git branch -rd origin / badbranch достаточно для уничтожения локальной ветки отслеживания или git push origin: badbranch для уничтожения удаленной ветки, Сделайте это, как только поймете, что они вам больше не понадобятся.

Из справки:

Динамические переменные, которые обычно управляются компилятором (длинные строки, широкие строки, динамические массивы, варианты и интерфейсы), могут быть объявлены с помощью threadvar, но компилятор не освобождает автоматически выделенную кучу память, создаваемая каждым потоком выполнения. Если вы используете эти типы данных в переменных потока, вы несете ответственность за удаление их памяти из потока, прежде чем поток завершится . Например,

threadvar S: AnsiString;
S := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  ...
S := '';  // free the memory used by S

Примечание: использование таких конструкций не рекомендуется.
Вы можете освободить вариант, установив для него значение Unassigned, а интерфейс или динамический массив - установив для него значение nil.

3
ответ дан 2 December 2019 в 21:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: