Помощь новичкам

Размерные на стеке, ссылочные - в управляемой динамической памяти

Всё равно не понятно.

  • Как работает трансформатор?
  • Примерно так: Ж-ж-ж-ж-ж-ж-ж…
1 лайк

Я серьёзно. Если в структуре есть хотя бы одно поле ссылочного типа, то она становится ссылочным типом. Какое тогда отличие структуры от класса?

Нет

А как тогда она хранит ссылочный тип? Разве что хранит ссылку?

+

1 лайк

В таком случае остался один вопрос - ссылка - это как указатель, число?

Да. Размер вроде зависит от системы. Если не ошибаюсь - на вин х32 она весит 32 бита, на х64 - 64 бита.

И ещё, не знаю как классы, но записи могут изменять размещение элементов, для оптимизации. К примеру:

type
  r1=record
    b1:integer;
    b2:byte;
  end;

begin
  writeln(sizeof(r1));
end.

У меня это выводит 8. Потому что на моему процессору под переменную b2 удобнее выделить 4 байта. (Кстати, это можно поменять с StructLayoutAttribute)

1 лайк

А я то думаю, почему же моя программа со структурой Color

Type Color=Record
Public L: SByte;
Public A: SByte;
Public B: SByte;
End;

Начала работать быстрее и кушатт меньше памяти после замены двумерного массива структур на трёхмерный массив SByte.

Ссылка – это особый “плавающий” указатель, значение которого (адрес, на который он указывает) автоматически корректируется сборщиком мусора в процессе дефрагментации управляемой динамической памяти (compacting GC), чтобы он продолжал указывать на тот же объект после его фактического перемещения сборщиком мусора.

Обычный “жесткий” указатель (pointer), если ему присвоить адрес какого-нибудь объекта в управляемой куче, в какой-то момент (после очередной сборки мусора и дефрагментации свободных участков кучи) просто “потеряет” свой объект и будет уже указывать неизвестно на что.

В исполняющей среде CLR (Common Language Runtime) существует 2 разных типа кучи: отдельно для малых объектов (SOH – Small Object Heap) и для больших (LOH – Large Object Heap). Чтобы не мешать друг другу, они располагаются в разных концах виртуальной памяти: первый – в нижних адресах, второй – в верхних адресах, и “растут” навстречу друг друг другу. В первом размещаются объекты размером менее 85 000 байт каждый, во втором от 85 000 байт и больше – в основном это длинные строки (т.е. ~ более 42000 символов в формате UTF-16) и длинные массивы (это особый случай – если более 1000 элементов любого типа).

При этом только первый тип кучи (SOH) подлежит автоматической дефрагментации при сборке мусора. Второй тип кучи (LOH) можно дефрагментировать только принудительно и только при работе под .NET 4.5.1 и новее.

В куче второго типа (LOH) куски памяти, занимаемые уже “мертвыми” большими объектами, в результате обычной фоновой сборки мусора только помечаются как свободные в специальном списке, образуя неиспользуемые “дыры” в виртуальной памяти (что может быть особенно критично для некоторых 32-битных приложений, учитывая ограниченную доступную адресацию для всего процесса ~2GB).

Чтобы уменьшить эффект “утечки памяти” в LOH из-за фрагментации (или даже риск краха всей системы в результате возникновения глобального OutOfMemoryException), при выделении памяти под очередной большой объект аллокатор LOH сперва должен просмотреть весь список свободных участков на предмет наличия “дырки” нужного размера. В случае же с выделением памяти в SOH аллокатору достаточно просто увеличить адрес указателя конца кучи.

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

Summary

TL;DR Только для настоящих сварщиков! :skull_and_crossbones: :sunglasses: Если очень нужно, то есть интересная альтернатива использованию стандартной большой кучи в CLR. Не уверен, правда, что эти библиотеки сгодятся для нашего Паскаля – слишком уж низкоуровневые вещи они затрагивают. Но сама статья о проблемах управляемой памяти зачетная!

2 лайка

Прекрасная статья!

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

Почему не работает такая программа?

function mySum<T>(a,b: T) := a + b;
begin  
  Println(mySum(2, 3))
end.

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

Ну, конечно. Разве у произвольного типа T есть +?

Тогда это только для обобщенных функций поиска с операциями сравнения можно так делать?

чего чего?
Обобщенные функции поиска?
И при чём тут операции сравнения?

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

Не совсем. operator> и operator< не работают для шаблонных типов. В сортировке сделано как то так:

function my_min<T>(o1,o2:T):T;
where T: System.IComparable;
begin
  Result := o1.CompareTo(o2)<0 ? o1 : o2;
end;

begin
  var m1 := my_min(2,3);             //нормально, потому что integer реализует IComparable
  var m2 := my_min(Arr(0),Arr(1));   //нет, потому что массивы не реализуют IComparable
end.

Только не в PaccalABC.NET ! Попробуйте написать обычную операцию отношения в обобщенном типа и увидите, куда пошлет Вас компилятор.

Те классы которые пишете вы - тоже могут реализовывать IComparable, для этого надо написать так:

  t1=class(System.IComparable)
    
  end;

Конечно, это вызовет ошибку, потому что как компилятор узнает - что именно сравнивать в типе t1. Чтоб узнать что надо реализовать - зажимаем Ctrl и нажимаем на IComparable:

type 
  /// Определяет обобщенный метод сравнения для типа, который реализуется типом или классом значения, чтобы упорядочить или сортировать его экземпляры.
IComparable = interface
{$region Методы}
  /// Сравнивает текущий экземпляр с другим объектом того же типа и возвращает целое число, которое показывает, расположен ли текущий экземпляр перед, после или на той же позиции в порядке сортировки, что и другой объект.
  /// Возвращаемое значение: Значение, указывающее, каков относительный порядок сравниваемых объектов.Возвращаемые значения представляют следующие результаты сравнения.Значение Описание Меньше нуля Этот экземпляр меньше параметра
  public function CompareTo(obj: System.Object): integer; virtual;

{$endregion}

Ага, значит у этого интерфейса есть только 1 функция - CompareTo. Вот её надо реализовать, чтоб работало. Копируем описание CompareTo, прямо всю строчку, в наш класс, но убираем virtual:

function my_min<T>(o1,o2:T):T;
where T: System.IComparable;
begin
  Result := o2.CompareTo(o1)>0 ? o1 : o2;
end;

type
  t1=class(System.IComparable)
    
    b:byte;
    
    public function CompareTo(obj: System.Object): integer;
    begin
      var a2 := obj as t1;
      Result := self.b - a2.b;//self можно не писать. Это лишь чтоб обозначит явно, что "b" берём из данного объекта
    end;
    
    constructor(b:byte) :=
    self.b := b;
    
  end;

begin
  var m1 := my_min(2,3);
  writeln(m1);
  
  var m2 := my_min(new t1(4),new t1(5));
  writeln(m2.b);
end.

Это выводит 2, а затем 4.