Управление размером сегмента данных
|
В компьютерном сервисном центре:
- Здравствуйте, я вам вчера в ремонт память сдавал...
- Какую именно?
- Не помню...
|
Тема: Управление размером сегмента данных
Ошибка "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 Как управлять размером сегмента данных?
- Избегайте объявления глобальных переменных и типизированных констант,
особенно больших массивов. Вместо этого объявляйте тип и указель на него. Затем
пользуйтесь функциями для работы с памятью, например, Getmem, которая
распределит вам память в глобальной куче. Это уменьшит использование ресурсов в
сегменте данных до 4 байт, необходимых для переменной указательного типа.
Смотрите ниже пример кода.
- Отдавайте себе отчет о влиянии компонентов. Смотри выше.
- Если у вас имеется множество строк, или массив строк, распределяйте это
динамически. Примечание: по умолчанию длина строки равна 255 символам --
объявляйте, где это возможно, специфический размер строки: (например,
MyShortString: string[20]).
- При необходимости работать с большим количеством строк рекомендуется
использовать объект TStringList.
- Тип Pchar, "указатель на строку", может быть использован для динамического
создания и управления символьными строками, используя при этом небольшое размер
сегмента данных. Смотри раздел электронной справки "String-handling routines
(Null-terminated)" (подпрограммы обработки строк (с терминирующим нулем)).
- Информация, необходимая для работы с памятью, доступна в 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;
|
|