2964:Managing Data Segment Size
KEYWORDS: data segment size AREA: General
Managing Data Segment Size
The "Data Segment too large" error occurs in Delphi 16
bit applications when the size of static data, stack and local
heap are larger than the 64k application limit imposed by
Windows. This document discusses how to identify and measure
portions of your code that consume memory in the data segment,
and how to manage this limited resource.
What does the data segment consist of?
--------------------------------------
Task header: 16 bytes of various Windows system information
Static data: Contains global variables and typed constants.
Stack: Stores local variables allocated by procedures
and functions. The default stack size is 16k
and can be modified in the
Options|Project|Linker page.
Local heap: Used by Windows for temporary storage and
defaults to 8k. Do not set the local heap to 0.
Windows may expand this area of memory if
necessary.
How do I measure total data segment size?
-----------------------------------------
To get the size of a 16 bit Delphi application static
data, stack and local heap for a project, compile the project
and select the Delphi menu item Compile|Information. The dialog
will list the following, given a new project with one form:
Source compile: 12 lines
Code size: 128981 bytes
Data size: 3636 bytes
Stack size: 16384 bytes
Local Heap size: 8192 bytes
A Delphi application starts with the overhead of static
data declared in units that provide the applications initial
functionality. If the only global variable is the form name,
the application will still consume at least 3,636 bytes.
Adding a second form increases the datasize to only 3640 --
an increase of only the size of the global variable needed to
declare a second form.
var
Form2: TForm2; { 4 byte pointer }
The total data segment size, (the sum of static data,
stack and local heap) is 28,212 bytes:
Data size: 3,636
Stack size: 16,384
Local Heap size: 8,192
------------------------------
28,212
What portions of my project increase data size?
-----------------------------------------------
-- Variables declared within the interface and implementation
sections.
-- Typed constants declared anywhere within the application.
An example of a typed constant declaration:
const
MyConst: integer = 100;
Units declared in the Uses clause and components
contain code that may hvae global variables or typed constants.
For instance, a TBitBtn adds 180 bytes when added to a project,
where at least 132 bytes are accounted for by typed constants
and global variables within the Buttons.Pas unit. Adding 10
more TBitBtns to the project does not increase the project
size beyond the initial 180 byte increase.
The following sample unit includes comments describing memory
usage:
unit Test;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
{ Functions used from the units may have global variables
and typed constants that will increase the size of the
data segment. }
type
{ Class objects are stored on the global heap, not the data
segment}
TForm1 = class(TForm)
Label1: TLabel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
public
{ MyInteger and MyString are stored
on the global heap. }
MyInteger: Integer;
MyString: String;
end;
const
{ MyConst is a typed constant and is stored in
static data portion of the data segment.
Minimize the number of typed constants. }
MyConst: integer = 23;
var
{ Form1 is a global variable - stored in the static
data portion of the data segment. You should minimize
the number and size of global variables. Form1 is
pointer and uses only four bytes. }
Form1: TForm1;
{ MyGlobalString will consume 256 bytes even if the
string is only a few characters long. }
MyGlobalString: string;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
{ MyLocal is a local variable and is not stored
in the data segment. }
MyLocal: String;
begin
MyLocal := 'Test application';
Label1.Caption := MyLocal
end;
end.
What is the impact of components on the data size?
--------------------------------------------------
Here is a list of components from the Standard, Additional,
Data Access, and Data Control(partial list) pages of the
component pallette. The list shows the compile size after
added a single instance of the component to a new project.
In descending order by data size usage:
Component App Bytes
in Over
bytes 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
How do I manage data segment size?
----------------------------------
1) Avoid declaring global variables or typed constants,
particularly large arrays. Instead, declare a type and
a pointer to that type. Then use memory management
routines such as Getmem to allocate memory from the
global heap.
This reduces the resource hit on the data segment to the
4 bytes used by the pointer variable. See code sample below.
2) Be aware of the impact of components. See above.
3) If you have a large number of strings, or arrays of strings,
allocate these dynamically. Note: strings default to 255 in
length -- declare as a specific size where possible:
(i.e. MyShortString: string[20]).
4) A TStringList object can be used to create and manipulate
large numbers of strings.
5) The Pchar type, "pointer to a string", can be used to
dynamically create and manipulate character strings using
little data segment space. See online help under
"String-handling routines (Null-terminated)".
6) Information on memory issues is available in Object Pascal
Language Guide, OBJLANG.ZIP and can be downloaded from
Compuserve, DELPHI forum, and the World Wide Web,
www.borland.com/TechInfo/delphi/whatsnew/dwnloads.html.
Alternative to global declaration of a large structure
------------------------------------------------------
Here's an example that will waste 32 bytes of data segment,
followed by a second example using only 4 bytes, and
accomplishes essentially the same task.
1)
{ Declaring TMyStructure causes no change
in the data segment size. }
TMyStructure = record
Name: String[10];
Data: array[0..9] of Integer;
end;
var
Form1: TForm1;
{ MyStructure declaration causes a 32 byte
increase:
Mystructure pointer = 1 byte
Name = 11 bytes (10 chars + length byte)
Data = 20 bytes (10 * 2 bytes per integer)
}
MyStructure: TMyStructure;
2)
{ Declaring TMyStructure causes no change
in the data segment size. }
PMyStructure = ^TMyStructure;
TMyStructure = record
Name: String[10];
Data: array[0..9] of Integer;
end;
var
Form1: TForm1;
{ MyDataPtr causes 4 byte increase
for the pointer variable. }
MyDataPtr: PMyStructure;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
{ Here, resources are taken from the heap. }
New(MyDataPtr);
MyDataPtr.Name := 'Fred';
MyDataPtr.array[0] := 560;
Dispose(MyDataPtr);
end;
You can also put a variable declaration within a class:
type
TMyBigArray = array[1..100] of string
TForm1 = class(TForm)
public
{ This declaration has no impact on data segment size. }
MyBigArray: TMyBigArray;
end;
var
{ This declaration increases data segment by 25,600 bytes. }
MyOtherBigArray: TMyBigArray;
TI