Как я могу найти размер памяти ссылаемым указателем?

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

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

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

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

Эти функции могут вызвать изменения состояния, которые могут быть свободно чередованы или, возможно, должны быть отлично вложены. Изменения состояния могут быть ориентированы на многопотоковое исполнение, или они не могли бы.

Взгляд на пример в вопросе Судьи. Изменения в добавлении отступа Файла журнала должны быть отлично вложены, или все это идет не так, как надо. Также они вряд ли будут ориентированы на многопотоковое исполнение.

возможно цеплять поездку со сборщиком "мусора" для очищения неуправляемых ресурсов. Но только если функции изменения состояния ориентированы на многопотоковое исполнение, и два состояния могут иметь время жизни то перекрытие всегда. Таким образом, пример Судьи ресурса не должен иметь финализатора! Это просто не помогло бы никому.

Для тех видов ресурсов, можно просто реализовать IDisposable без финализатора. Финализатор является абсолютно дополнительным - это должно быть. Это приукрашено или даже не упомянуто во многих книгах.

затем необходимо использовать using оператор, чтобы иметь любой шанс обеспечения, которое Dispose называют. Это по существу похоже на подтягивание поездки со стеком (поэтому, как финализатор к GC, using к стеку).

недостающая часть - то, что необходимо вручную записать, Располагают и заставляют его звонить на поля и базовый класс. C++ / программисты CLI не должен делать этого. Компилятор пишет это для них в большинстве случаев.

существует альтернатива, которая я предпочитаю для состояний, чтобы вложенное множество отлично и не было ориентировано на многопотоковое исполнение (кроме чего-либо еще, избегая, чтобы IDisposable сэкономил Вас проблема наличия спора с кем-то, кто не может сопротивляться добавлению финализатора к каждому классу, который реализует IDisposable).

Вместо того, чтобы писать класс, Вы пишете функцию. Функция принимает, что делегат перезванивает к:

public static void Indented(this Log log, Action action)
{
    log.Indent();
    try
    {
        action();
    }
    finally
    {
        log.Outdent();
    }
}

И затем простой пример был бы:

Log.Write("Message at the top");
Log.Indented(() =>
{
    Log.Write("And this is indented");

    Log.Indented(() =>
    {
        Log.Write("This is even more indented");
    });
});
Log.Write("Back at the outermost level again");

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

Эта техника менее полезна, если ресурс является видом, который может иметь перекрывающееся время жизни, потому что затем Вы хотите смочь создать ресурс A, затем ресурс B, затем уничтожить ресурс A и затем позже уничтожить ресурс B. Вы не можете сделать этого при принуждении пользователя к отлично вложенному множеству как это. Но затем необходимо использовать IDisposable (но все еще без финализатора, если Вы не реализовали threadsafety, который не свободен).

8
задан Mason Wheeler 23 August 2009 в 18:55
поделиться

5 ответов

Казалось бы, размер блока, на который ссылается указатель, возвращаемый GetMem () , должен быть откуда-то доступен, учитывая, что FreeMem () не требует, чтобы вы определяли размер освобождаемой памяти - система должна иметь возможность определить это, так почему бы не разработчику приложения?

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

Диспетчер памяти по умолчанию будет поддерживать необходимую информацию любым удобным для него способом, но какой-то другой диспетчер памяти почти наверняка будет использовать совершенно другой, хотя и внешне похожий механизм, поэтому даже если вы взломаете решение, основанное на глубоком знании одного диспетчер памяти, если вы измените диспетчер памяти (или если он будет изменен для вас, например, путем изменения определенной системы, диспетчера памяти, который вы, возможно, используете по умолчанию, например, как это произошло между Delphi 2005 и 2006), тогда ваше решение почти наверняка сломается.

В общем, со стороны диспетчера RTL / памяти вполне обоснованно предположение, что приложение уже должно знать, какой размер памяти относится к выделенному указателю GetMem () к, учитывая, что приложение просило его в первую очередь! : )

И если ваше приложение НЕ выделяло указатель, то диспетчер памяти вашего приложения не имеет абсолютно никакого способа узнать, насколько большим может быть блок, на который он ссылается. Это может быть указатель на середину некоторого большего блока, например - только источник указателя может знать, как он соотносится с памятью, на которую он ссылается!

Но если ваше приложение действительно это делает необходимо поддерживать такую ​​информацию о своих собственных указателях, тогда он, конечно, мог бы легко разработать средства для достижения этого с помощью простого одноэлементного класса или библиотеки функций, с помощью которых GetMem () / ] Запросы FreeMem () маршрутизируются, чтобы поддерживать запись соответствующего запрашиваемого размера для каждого текущего выделенного указателя. Такой механизм, конечно, мог бы легко раскрыть эту информацию по мере необходимости, полностью надежно и независимо от того, какой менеджер памяти используется.

Это может быть фактически единственным вариантом, если запись « точная » является требуется, поскольку данная реализация диспетчера памяти может выделить больший блок памяти для заданного размера данных, чем фактически запрашивается. Я не знаю, действительно ли это делает какой-либо менеджер памяти, но теоретически он мог бы делать это в целях эффективности.

8
ответ дан 5 December 2019 в 13:00
поделиться

Он предназначен для внутреннего использования, поскольку зависит от используемого MemoryManager. Кстати, вот почему вам нужно использовать пару GetMem / FreeMem из одного и того же MemoryManager; не существует канонического способа узнать, как была зарезервирована память.
В Delphi, если вы посмотрите на FastMM4, вы увидите, что память распределяется маленькими, средними или большими блоками:
малых блоков распределяются в пулах блоков фиксированного размера (размер блока определяется на уровне пула в типе блока)

  TSmallBlockType = packed record
    {True = Block type is locked}
    BlockTypeLocked: Boolean;
    {Bitmap indicating which of the first 8 medium block groups contain blocks
     of a suitable size for a block pool.}
    AllowedGroupsForBlockPoolBitmap: byte;
    {The block size for this block type}
    BlockSize: Word;

средние блоки также выделяются в пулах, но имеют переменный размер

  {Medium block layout:
   Offset: -8 = Previous Block Size (only if the previous block is free)
   Offset: -4 = This block size and flags
   Offset: 0 = User data / Previous Free Block (if this block is free)
   Offset: 4 = Next Free Block (if this block is free)
   Offset: BlockSize - 8 = Size of this block (if this block is free)
   Offset: BlockSize - 4 = Size of the next block and flags

  {Get the block header}
  LBlockHeader := PCardinal(Cardinal(APointer) - BlockHeaderSize)^;
  {Get the medium block size}
  LBlockSize := LBlockHeader and DropMediumAndLargeFlagsMask;

большие блоки выделяются индивидуально с требуемым размером

  TLargeBlockHeader = packed record
    {Points to the previous and next large blocks. This circular linked
     list is used to track memory leaks on program shutdown.}
    PreviousLargeBlockHeader: PLargeBlockHeader;
    NextLargeBlockHeader: PLargeBlockHeader;
    {The user allocated size of the Large block}
    UserAllocatedSize: Cardinal;
    {The size of this block plus the flags}
    BlockSizeAndFlags: Cardinal;
  end;
5
ответ дан 5 December 2019 в 13:00
поделиться

Эта информация предназначена только для внутреннего использования, или есть ли способ получить размер буфера, на который указывает указатель?

Противоречат ли эти две "альтернативы" друг другу?

Только для внутреннего использования.

0
ответ дан 5 December 2019 в 13:00
поделиться

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

Я могу представить, что формат изменен другой версией диспетчера памяти, поэтому не рассчитывайте на это.

0
ответ дан 5 December 2019 в 13:00
поделиться

Эта информация предназначена только для внутреннего использования.

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

0
ответ дан 5 December 2019 в 13:00
поделиться
Другие вопросы по тегам:

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