Советы по Delphi

         

Управление размером сегмента данных


Тема: Управление размером сегмента данных

Ошибка "Data Segment too large" (сегмент данных слишком велик) возникает в Delphi 16-битных приложениях в случае, когда размер статических данных, стека и локальной кучи превышает предел приложений Windows, установленный в 64К. В данном совете обсуждается тема идентификации и изменения части вашего кода, которая поглощает память в сегменте данных, и как, собственно, управлять этим ограниченным ресурсом.

Из чего состоит сегмент данных? Task header: 16 байт различной системной информации Windows (заголовок задачи) Static data: Содержит глобальные переменные и типовые (статические константы данные) Stack: Хранит локальные переменые, распределенные (стек) процедурами и функциями. Размер стека по по умолчанию 16К и может быть изменен на странице Options|Project|Linker. Local heap: Используется Windows для временного хранения и (локальная по умолчанию имеет размер 8К. Не устанавливайте куча) разнер локальной кучи, равным 0. Windows при необходимости может увеличить данную область. Как мне узнать полный размер сегмента данных?

Для того, чтобы получить размер статических данных 16-битного приложения Delphi, стека и локальной кучи для проекта, скомпилируйте проект и выберите в меню Delphi пункт Compile|Information. Для нового проекта с одной формой диалог покажет следующую информацию: Source compile: 12 lines (скомпилировано 12 строк исходного кода) Code size: 128981 bytes (размер кода 128981 байт) Data size: 3636 bytes (размер данных 3636 байт) Stack size: 16384 bytes (размер стека 16384 байт) Local Heap size: 8192 bytes (размер локальной кучи 8192 байт) Приложение Delphi начинается с объявления в модуле статических данных, тем самым обеспечивая функциональную инициализацию. Если единственная глобальная переменная является именем формы, то приложение занимает уже, по крайней мере, 3,636 байт. Добавляя вторую форму, размер увеличивается только до 3640 -- добавляется только размер глобальной переменной, необходимой для объявления второй формы.



    var
Form2: TForm2; { 4-х байтный указатель }

Общий размер сегмента данных (сумма статических данных, стека и локальной кучи) составляет 28,212 байт: Data size: 3,636 Stack size: 16,384 Local Heap size: 8,192 ----------------------- 28,212 Какие части моего проекта увеличивают размер данных?

  • Переменные, объявленные в секции interface и implementation.
  • Типизированные константы, объявленные в любом месте приложения.
Пример объявления типизированной константы:

    const MyConst: integer = 100;

Модули, объявленные в списке Uses, и компоненты могут содержать код с объявлениями глобальных переменных или типизированных констант. Например, TBitBtn добавляет к проекту 180 байт, и, по крайней мере, 132 байт резервируются для типизированных констант и глобальных переменных, объявленных в модуле Buttons.Pas. При добавлении дополнительных 10 компонентов TBitBtn, размер проекта после первых 180 байт не увеличится.

Следующий демонстрационный модуль включает в себя комментарии, описывающие использование памяти:

    unit Test;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
{ Используемые в модуле функции могут иметь глобальные переменные и типизированные константы, которые увеличивают размер сегмента данных. }

type { Объекты класса хранятся в глобальной куче, не в сегменте данных} TForm1 = class(TForm) Label1: TLabel; Button1: TButton; procedure Button1Click(Sender: TObject); public { MyInteger и MyString хранятся в глобальной куче. } MyInteger: Integer; MyString: String; end;
const { MyConst - типизированная константа и сохраняется в области статических данных сегмента.
Минимизируйте количество типизированных констант. } MyConst: integer = 23;
var
{ Form1 - глобальная переменная и хранится в области статических данных сегмента. Вы должны минимизировать число и размер глобальных переменных. Form1 является указателем и занимает только четыре байта. }
Form1: TForm1; { MyGlobalString занимает 256 байт, даже если строка состояла бы всего из нескольких символов. }
MyGlobalString: string;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject); var { MyLocal - локальная переменная и не хранится в сегменте данных. } MyLocal: String; begin MyLocal := 'Тестовое приложение'; Label1.Caption := MyLocal end;
end.

Как влияют компоненты на размер данных?

Вот список компонентов со страниц Standard, Additional, Data Access и Data Control (часть списка) палитры компонентов. В списке приведен размер компилиции после добавления к новому проекту единственного экземпляра компонента. Список отсортирован согласно количеству расходуемой памяти: Компонент Прил. Байтов в свыше байт. 3,636 table 4272 636 batchmove 4272 636 storedproc 4258 622 query 4250 614 database 4036 400 datasource 3886 250 outline 3838 202 bitbtn 3816 180 stringgrid 3794 158 drawgrid 3790 154 maskedit 3762 126 memo 3750 114 report 3722 86 listbox 3704 68 edit 3694 58 tabset 3692 56 combobox 3674 38 scrollbar 3654 18 button 3652 16 checkbox 3652 16 radiobutton 3652 16 radiogroup 3652 16 panel 3650 14 label 3648 12 speedbutton 3646 10 header 3644 8 scrollbox 3644 8 notebook 3638 2 menu 3636 0 groupbox 3636 0 tabbednotebook 3636 0 image 3636 0 shape 3636 0 Как управлять размером сегмента данных?

  1. Избегайте объявления глобальных переменных и типизированных констант, особенно больших массивов. Вместо этого объявляйте тип и указель на него. Затем пользуйтесь функциями для работы с памятью, например, Getmem, которая распределит вам память в глобальной куче. Это уменьшит использование ресурсов в сегменте данных до 4 байт, необходимых для переменной указательного типа. Смотрите ниже пример кода.
  2. Отдавайте себе отчет о влиянии компонентов. Смотри выше.
  3. Если у вас имеется множество строк, или массив строк, распределяйте это динамически. Примечание: по умолчанию длина строки равна 255 символам -- объявляйте, где это возможно, специфический размер строки: (например, MyShortString: string[20]).
  4. При необходимости работать с большим количеством строк рекомендуется использовать объект TStringList.
  5. Тип Pchar, "указатель на строку", может быть использован для динамического создания и управления символьными строками, используя при этом небольшое размер сегмента данных. Смотри раздел электронной справки "String-handling routines (Null-terminated)" (подпрограммы обработки строк (с терминирующим нулем)).
  6. Информация, необходимая для работы с памятью, доступна в Object Pascal Language Guide (руководство по языку Object Pascal), OBJLANG.ZIP и может быть загружена из Compuserve, форум DELPHI, или из Интернета по адресу www.borland.com/TechInfo/delphi/whatsnew/dwnloads.html.
Альтернатива для глобального объявления большой структуры

Первый пример расходует 32 байта сегмента данных, второй всего-лишь 4 байта, но выполняет ту же задачу.

  1.     { Объявление TMyStructure не вызовет никаких изменений в размере сегмента данных. } TMyStructure = record Name: String[10]; Data: array[0..9] of Integer; end;
    var Form1: TForm1;
    { Объявление MyStructure вызовет увеличение размера сегмента памяти на 32 байта: указатель Mystructure = 1 байт Name = 11 байт (10 символов + байт длины) Data = 20 байт (10 * 2 байт на целое) } MyStructure: TMyStructure;

  •     { Объявление TMyStructure не вызовет никаких изменений в размере сегмента данных. } PMyStructure = ^TMyStructure; TMyStructure = record Name: String[10]; Data: array[0..9] of Integer; end;
    var Form1: TForm1; { MyDataPtr вызывает увеличение на 4 байта для размещения указательной переменной. } MyDataPtr: PMyStructure;
    implementation
    {$R *.DFM}
    procedure TForm1.FormCreate(Sender: TObject); begin { Здесь ресурсы берутся из кучи. } New(MyDataPtr); MyDataPtr.Name := 'Fred'; MyDataPtr.array[0] := 560; Dispose(MyDataPtr); end;
  • Вы также можете разместить объявление переменной в пределах класса:

        type TMyBigArray = array[1..100] of string
    TForm1 = class(TForm) public { Это объявление не повлияет на размер сегмента данных. } MyBigArray: TMyBigArray; end;
    var { Это объявление увеличивает сегмент данных на 25,600 байт. } MyOtherBigArray: TMyBigArray;

    [000944]



    Содержание раздела