| Summary: | - When you instantiate an object from a class, storage
for the object is allocated from an area of memory.
- The Curl® language has a
garbage collection system that eliminates
the task of micro-managing the memory use of your
source code.
- The weak pointer feature allows you to store data in
memory while having it available for garbage
collection and reallocation.
|
Unprivileged Curl applets are limited to 1GB of memory. See
System
Restrictions. Within that limit, when you instantiate an object
from a class, the memory to store the object is allocated from an
area of memory. The amount of memory used for the object depends
on the object's fields. For example, the object instantiated in
the following source code requires at least enough memory to store
an
int and two
chars:
{define-class public MemoryExample1
field public a:int
field public b:char
field public c:char
}
let myobj:MemoryExample1={MemoryExample1}
The object reference variable that points to an instantiated object
(such as
myobj in the previous example) itself takes up memory
to point to the memory address of the instantiated object. This is an
important distinction. In the following class:
{define-class public MemoryExample2
field public x:int
field public z:MemoryExample1
}
The field z in the class definition is a reference to an object
of the class MemoryExample1. An instance of
MemoryExample2 will allocate memory to store an int and a
object reference, not an entire instance of MemoryExample1.
The address space used by Curl applets is 32 bit on both 32 and 64
bit machines.
In some languages, such as C, you must explicitly allocate memory
when creating a new object, and free the memory when your program is
done with it. A lot of your programming time is spent in managing
memory.
In these languages, failing to free some memory after your program is
done with it results in a
memory leak,
where bits of
memory are no longer used by the program but are not reclaimed and
reused by the operating system. Eventually, memory leaks can cause
the system to run out of memory; however, in the Curl language, the following code is perfectly safe:
{define-class public MyClass
field public a:int
field public b:char
field public c:char
}
{value
let myobj:MyClass={MyClass}
set myobj={MyClass}
set myobj={MyClass}
}
This code creates three objects of the class MyClass. The
first two instances are unreferenced or inaccessible, since the object reference myobj no
longer points to them. Therefore, the program has no way of regaining access
to them. They have become garbage.
Fortunately, the Curl language has a garbage collection system. Periodically, the
garbage collector scans the memory area, looking for memory that is no
longer in use. Any memory that does not have at least one object reference
pointing to it is reclaimed by the garbage collector
so it can be reallocated later.
The garbage collection system frees you from the chore of having to
micro-manage the memory use of your code. However, you need to consider two
potential downsides of garbage collection:
- There is no guarantee that all garbage
will be collected, or that it will be collected in a timely manner.
- A garbage collection pass can cause program execution to pause
for a fraction of a second or sometimes even several seconds.
To address the issues mentioned above, you can use the
garbage-collect procedure to suggest to the system that garbage
collection take place at a certain point in your program. You might
call
garbage-collect after a piece of code that has used a
lot of memory that it no longer needs, or before a speed-critical
section where a garbage collection pause may cause a problem.
The following example demonstrates using
garbage-collect. Clicking
Make Garbage generates at least
1MB of garbage by instantiating and then
losing
many
1024-character arrays. Clicking
Clean Garbage calls the
garbage-collect procedure to clean up the mess the first
button creates.
In order for you to see the effects of this example, you will need
some way to monitor the memory usage on your system, such as the Task
Manager window under Windows NT. Click Make Garbage and look
for an increase in the
Curl® Runtime Environment (RTE) memory use. Then
click Clean Garbage to see its memory use decline again.
| Example:
Using garbage-collect |
 |
{value
let make-garbage:CommandButton =
{CommandButton label = "Make Garbage",
{on Action do
{for x = 1 to 1000 do
let a:{Array-of char} = {new {Array-of char}}
{a.set-size 1024, 'x'}
}
}
}
let clean-garbage:CommandButton =
{CommandButton label = "Clean Garbage",
{on Action do
{garbage-collect}
}
}
{HBox make-garbage, clean-garbage}
}
| |
When planning memory use in your applets written in the
Curl® language, you may have data
that would be useful but not absolutely vital to store in memory. By
storing information in memory rather than having to reread it or
recalculate it, your application will run faster. However, if the
system is running low on memory, storing large amounts of data that
may not be reused and can be regenerated if needed becomes a liability.
The Curl language's weak pointer feature lets you store data in memory, while having
it available for garbage collection and reallocation if the garbage
collector runs. In addition to collecting memory that is no longer used,
the garbage collector also reclaims any memory that is only referenced by
weak pointers.
To use weak pointers, you use the
weak modifier when declaring
the class field that you want to be weak:
{define-class public WeakData
field private weak _myweakdata:#{HashTable-of String, String}
}
If the weak pointer's data is garbage collected, it will have a value of
null. Therefore, you must
always use the
#
operator to indicate the weak pointer can accept a value of
null. The compiler will throw an error if you forget the
#.
You should never store irreplaceable data in an object referenced by a
weak pointer. Only use weak pointers with data that can be
recalculated or otherwise recreated without too much overhead. When
deciding whether to use a weak pointer to store data, you need to
consider the tradeoff between possibly having to recalculate the data
in the object again if it is garbage collected, and having the data
take up space in memory that could be used for other, more vital
information.
If the data referenced by a weak pointer is garbage collected, its
object reference variable is set to
null. Before you try to access
data referenced by only a weak pointer, you need to check the object
reference to make sure the data is still there. If it is not, you will
have to recreate the data. You should always mark fields that are weak
pointers to be
private, since their contents should only be
read through an accessor.
Even after you check the object reference to ensure it is not
null, the data could be garbage collected at any time while you are
working with it. To prevent the garbage collector from collecting the
data while you are accessing it, you should create a new, non-weak
object reference pointing to the data. The non-weak pointer to the
object prevents the garbage collector from collecting the garbage.
When you are through accessing the cache data, you can eliminate the
non-weak pointer (usually by exiting its scope, such as a method or a
procedure). Once the non-weak reference to the data is gone, the data
will again be available for garbage collection.
The safe way to access data referenced by a weak pointer is:
- Create a new object reference variable and set it to the
weak pointer.
- Check the new object reference to see if it is null.
If it is, the data has been purged by the garbage collector.
You need to recreate the data and set the weak pointer to the
new instance of the data.
- Retrieve the data you need via the new object reference.
- Eliminate the temporary pointer. This is usually done by exiting
the scope in which it was created.
The following example demonstrates how you can use weak pointers
to create a data cache class. The
MyCache class contains a
private field that is a weak reference to a
HashTable-of
Strings. A private method in the class,
make-lots-of-data is responsible for filling the
HashTable with whatever data it needs to contain. The constructor
of the class simply calls this private method to set up the cache
initially.
The class only allows access to the cached data via a method that takes
a
String parameter and returns the corresponding string in the
HashTable. This method checks to see if the private cache field
is
null. If it is, then the cache has been garbage collected, and
needs to be rebuilt by calling the
make-lots-of-data method
again.
{define-class public MyCache
|| The field that will hold the cached data
field private weak _cached-data:#{HashTable-of String, String}
|| The private method that generates the cached data
{method private {make-lots-of-data}:{HashTable-of String, String}
|| Lots of data generated here, stored in a HashTable and the
|| HashTable is returned.
}
|| The constructor calls make-lots-of-data to have the cache
|| filled after the object is created. You could defer this to
|| be when the object is first accessed, as well.
{constructor {default}
|| compute contents of cached-data by calling a private method
set self._cached-data={self.make-lots-of-data}
}
|| This method is called when people want to access the
|| cached data. Here, we accept a string and return the
|| associated string from the cached HashTable.
{method public {get-cache-element index:String}:String
|| Create a temporary reference to the cached data that
|| isn't weak, to prevent the cache from being garbage
|| collected while it is being accessed.
let tmp-cache-pointer:#{HashTable of String, String}=
self._cached-data
{if tmp-cache-pointer == null then
|| It has been garbage collected! Recalculate
set tmp-cache-pointer = {self.make-lots-of-data}
|| Set the weak cache pointer to the new copy of the
|| cache
set self._cached-data = tmp-cache-pointer
}
|| Return the data from the cache. Of course, in actual use,
|| you would perform error checking here to make sure the element
|| exists in the cache! You could also add code to calculate
|| requested elements not already in the cache, and store the
|| results into the cache as well as returning them.
{return {tmp-cache-pointer.get index}}
|| After exiting this method, the tmp-cache-pointer will no longer
|| exist, and therefore the cache data will again be available
|| for garbage collection.
}
}
Copyright © 1998-2019 SCSK Corporation.
All rights reserved.
Curl, the Curl logo, Surge, and the Surge logo are trademarks of SCSK Corporation.
that are registered in the United States. Surge
Lab, the Surge Lab logo, and the Surge Lab Visual Layout Editor (VLE)
logo are trademarks of SCSK Corporation.