Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
Управление размером сегмента данных

В компьютерном сервисном центре:
- Здравствуйте, я вам вчера в ремонт память сдавал...
- Какую именно?
- Не помню...

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

Ошибка "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 байта, но выполняет ту же задачу.


{ Объявление 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;

Проект Delphi World © Выпуск 2002 - 2004
Автор проекта: ___Nikolay