1
|
1 |
"
|
5
|
2 |
COPYRIGHT (c) 1992 by Claus Gittinger
|
159
|
3 |
All Rights Reserved
|
1
|
4 |
|
|
5 |
This software is furnished under a license and may be used
|
|
6 |
only in accordance with the terms of that license and with the
|
|
7 |
inclusion of the above copyright notice. This software may not
|
|
8 |
be provided or otherwise made available to, or used by, any
|
|
9 |
other person. No title to or ownership of the software is
|
|
10 |
hereby transferred.
|
|
11 |
"
|
|
12 |
|
|
13 |
Object subclass:#ObjectMemory
|
|
14 |
instanceVariableNames:''
|
2
|
15 |
classVariableNames:'InternalErrorHandler UserInterruptHandler TimerInterruptHandler
|
159
|
16 |
SpyInterruptHandler StepInterruptHandler ExceptionInterruptHandler
|
|
17 |
ErrorInterruptHandler MemoryInterruptHandler SignalInterruptHandler
|
|
18 |
ChildSignalInterruptHandler DisposeInterruptHandler
|
|
19 |
RecursionInterruptHandler IOInterruptHandler
|
|
20 |
CustomInterruptHandler
|
2
|
21 |
|
178
|
22 |
AllocationFailureSignal LowSpaceSemaphore
|
270
|
23 |
IncrementalGCLimit FreeSpaceGCLimit BackgroundCollectProcess
|
159
|
24 |
Dependents
|
|
25 |
ImageName'
|
1
|
26 |
poolDictionaries:''
|
|
27 |
category:'System-Support'
|
|
28 |
!
|
|
29 |
|
|
30 |
ObjectMemory comment:'
|
5
|
31 |
COPYRIGHT (c) 1992 by Claus Gittinger
|
159
|
32 |
All Rights Reserved
|
93
|
33 |
|
282
|
34 |
$Header: /cvs/stx/stx/libbasic/Attic/ObjMem.st,v 1.26 1995-02-24 16:37:15 claus Exp $
|
2
|
35 |
'!
|
|
36 |
|
|
37 |
!ObjectMemory class methodsFor:'documentation'!
|
|
38 |
|
88
|
39 |
copyright
|
|
40 |
"
|
|
41 |
COPYRIGHT (c) 1992 by Claus Gittinger
|
159
|
42 |
All Rights Reserved
|
88
|
43 |
|
|
44 |
This software is furnished under a license and may be used
|
|
45 |
only in accordance with the terms of that license and with the
|
|
46 |
inclusion of the above copyright notice. This software may not
|
|
47 |
be provided or otherwise made available to, or used by, any
|
|
48 |
other person. No title to or ownership of the software is
|
|
49 |
hereby transferred.
|
|
50 |
"
|
|
51 |
!
|
|
52 |
|
|
53 |
version
|
|
54 |
"
|
282
|
55 |
$Header: /cvs/stx/stx/libbasic/Attic/ObjMem.st,v 1.26 1995-02-24 16:37:15 claus Exp $
|
88
|
56 |
"
|
|
57 |
!
|
|
58 |
|
2
|
59 |
documentation
|
|
60 |
"
|
68
|
61 |
This class contains access methods to the system memory -
|
|
62 |
in previous versions this stuff used to be in the Smalltalk class.
|
|
63 |
It has been separated for better overall structure.
|
229
|
64 |
There are no instances of ObjectMemory - all is done in class methods.
|
1
|
65 |
|
68
|
66 |
Many methods here are for debuging purposes only, and not standard.
|
|
67 |
Do not depend on them being there - some may vanish ...
|
|
68 |
(especially those, that depend on a specific GC implementation)
|
13
|
69 |
|
229
|
70 |
Warning:
|
|
71 |
The InterruptHandler variables are known by the runtime system -
|
|
72 |
they are the objects that get an interrupt message when the event
|
|
73 |
occurs. You may not remove them.
|
2
|
74 |
|
229
|
75 |
Class variables:
|
2
|
76 |
|
159
|
77 |
InternalErrorHandler gets informed (by VM), when some runtime
|
|
78 |
error occurs (usually fatal)
|
2
|
79 |
|
159
|
80 |
UserInterruptHandler gets informed (by VM) when CNTL-C is pressed
|
|
81 |
TimerInterruptHandler gets alarm timer interrupts (from VM)
|
|
82 |
SpyInterruptHandler another alarm timer (from VM)
|
|
83 |
StepInterruptHandler gets single step interrupts (from VM)
|
|
84 |
ExceptionInterruptHandler gets floating point exceptions (from VM)
|
|
85 |
ErrorInterruptHandler gets graphic device errors (from VM)
|
|
86 |
MemoryInterruptHandler gets soon-out-of-memory conditions (from VM)
|
|
87 |
SignalInterruptHandler gets unix signals (from VM)
|
|
88 |
ChildSignalInterruptHandler gets child death signals (from VM)
|
|
89 |
DisposeInterruptHandler gets informed, when an object is disposed from
|
|
90 |
a shadowArray (from VM)
|
|
91 |
RecursionInterruptHandler gets recursion limit violations (from VM)
|
|
92 |
IOInterruptHandler gets SIGIO unix signals (from VM)
|
|
93 |
CustomInterruptHandler gets custom interrupts (from VM)
|
2
|
94 |
|
270
|
95 |
IncrementalGCLimit number of bytes, that must be allocated since
|
159
|
96 |
last full garbage collect to turn on incremental
|
|
97 |
collector.
|
270
|
98 |
|
159
|
99 |
Dependents keep my dependents locally (its faster) for
|
|
100 |
all those registries
|
270
|
101 |
|
213
|
102 |
LowSpaceSemaphore a semaphore signalled whenever the system is
|
|
103 |
running in low memory (i.e. the memory manager
|
|
104 |
ran into memory shortage and feels that it
|
|
105 |
may soon be no longer grant allocation requests).
|
|
106 |
You can have a process waiting on this semaphore
|
|
107 |
which starts to remove (i.e. nil-out) objects
|
|
108 |
or preform other cleanup actions.
|
|
109 |
|
|
110 |
AllocationFailureSignal signal raised when a new fails (see Behavior)
|
|
111 |
When this signal is raised, the meomory manager
|
|
112 |
is really in trouble (i.e. above feelings where
|
|
113 |
correct)
|
2
|
114 |
"
|
93
|
115 |
!
|
|
116 |
|
|
117 |
caching
|
|
118 |
"
|
|
119 |
The system uses various caches to speed up method-lookup.
|
|
120 |
Currently, there is a three-level cache hierarchy:
|
|
121 |
|
159
|
122 |
inline-cache keeps the target of the last send at the caller-
|
|
123 |
side (i.e. every send goes through its private
|
|
124 |
1-slot inline-cache, where the address of the last
|
|
125 |
called function at this call location is kept.)
|
93
|
126 |
|
229
|
127 |
polymorph-inline-cache keeps a limited list of all targets ever reached
|
|
128 |
at this call location. The list is automatically
|
|
129 |
flushed if it grows too large, or the overall number
|
|
130 |
of poly-chache entries exceeds a limit.
|
93
|
131 |
|
159
|
132 |
method-lookup-cache a global cache. Hashes on class-selector pairs,
|
|
133 |
returning the target method.
|
93
|
134 |
|
|
135 |
Whenever methods are added or removed from the system, or the inheritance
|
|
136 |
hierarchy changes, some or all caches have to be flushed.
|
202
|
137 |
The flushXXX methods perform the task of flushing various caches.
|
207
|
138 |
All standard methods in Behavior call for cache flushing, when things change;
|
229
|
139 |
however, if you use the low level access methods in Behavior
|
|
140 |
(for example: #setSuperclass:) special care has to be taken.
|
|
141 |
|
|
142 |
In some situations, not all caches need flushing, for example a change
|
|
143 |
in an interpreted method (currently) needs no flushing of the inline caches.
|
|
144 |
Also, flushing can be limited to entries for a specific class for most changes.
|
|
145 |
|
202
|
146 |
To be 'on the brigth side of live', use ObjectMemory>>flushCaches (which
|
|
147 |
flushes all of them), when in doubt of which caches should be flushed.
|
|
148 |
It is better flush too much - otherwise you may end up in a wrong method after
|
|
149 |
a send.
|
93
|
150 |
"
|
|
151 |
!
|
|
152 |
|
|
153 |
interrupts
|
|
154 |
"
|
|
155 |
Handling of interrupts (i.e. unix-signals) is done via handler objects, which
|
|
156 |
get a #XXXInterrupt-message sent. This is more flexible than (say) signalling
|
|
157 |
a semaphore, since the handler-object may do anything to react on the signal
|
|
158 |
(of course, it can also signal a semaphore to emulate the above behavior).
|
|
159 |
|
229
|
160 |
Another reason for having handler objects is that they allow interrupt handling
|
207
|
161 |
without any context switch, for high speed interrupt response.
|
|
162 |
However, special care is needed, since it is not defined, which process gets
|
|
163 |
the interrupt and will do the processing.
|
93
|
164 |
Typically, the handlers are set during early initialization of the system
|
202
|
165 |
by sending 'ObjectMemory XXXInterruptHandler:aHandler' and not changed later.
|
|
166 |
(see Smalltalk>>initialize or ProcessorScheduler>>initialize).
|
207
|
167 |
To setup your own handler, create some object which responds to #xxxInterrupt,
|
|
168 |
and make it the handler using the above method.
|
|
169 |
|
|
170 |
Interrupt messages sent to handlers are:
|
|
171 |
internalError:<someString> - internal interpreter/GC errors
|
|
172 |
userInterrupt - ^C interrupt
|
|
173 |
customInterrupt - custom interrupt
|
|
174 |
ioInterrupt - SIGIO interrupt
|
229
|
175 |
timerInterrupt - alarm timer (SIGALRM)
|
207
|
176 |
errorInterrupt - display error
|
229
|
177 |
spyInterrupt - spy timer interrupt (SIGVTALARM)
|
207
|
178 |
stepInterrupt - single step interrupt
|
|
179 |
disposeInterrupt - finalization required
|
|
180 |
recursionInterrupt - recursion (stack) overflow
|
|
181 |
memoryInterrupt - soon running out of memory
|
229
|
182 |
fpExceptionInterrupt - floating point exception (SIGFPE)
|
207
|
183 |
childSignalInterrupt - death of a child process (SIGCHILD)
|
|
184 |
signalInterrupt:<number> - unix signal (if other than above signals)
|
93
|
185 |
"
|
|
186 |
!
|
|
187 |
|
|
188 |
garbageCollection
|
|
189 |
"
|
|
190 |
Currently, Smalltalk/X uses a two-level memory hierachy.
|
|
191 |
Objects are created in a so-called newSpace, which is relatively small.
|
202
|
192 |
This newSpace is cleaned by a scavenge-operation, whenever becoming
|
133
|
193 |
full. Scavenging means, that all still-live objects (i.e. referenced by some
|
|
194 |
other) are copied over to another memory area, leaving all unreferenced
|
207
|
195 |
objects as garbage behind. After this copying, these two semispaces exchange their
|
202
|
196 |
roles - i.e. objects are copied ping-pong like between these semispaces.
|
|
197 |
Once an object survives enough of these copying operations, the next scavenge
|
216
|
198 |
will move it into the so called oldSpace, which is much larger, and not
|
202
|
199 |
processed by the scavenger.
|
|
200 |
This movement of an object from newSpace to oldSpace is called 'tenure'.
|
133
|
201 |
|
93
|
202 |
Scavenging occurs automatically, and is usually done fast enough to go
|
|
203 |
unnoticed (typically, it takes some 5 to 50ms to perform a scavenge,
|
|
204 |
depending on how many live objects are in the newspace).
|
|
205 |
Interrestingly, the scavenger performs better, if many garbage objects
|
|
206 |
are to be reclaimed, since less object-copying has to be done. Therefore,
|
216
|
207 |
the best-case scavenge time is almost zero, if there is only garbage in
|
|
208 |
the newSpace. In contrast, the worst-case is when all newSpace objects are still
|
207
|
209 |
living. To honor this situation, the system uses an adaptive tenure-count,
|
|
210 |
which adjusts the number of scavenges required for tenure (the so called
|
|
211 |
'tenureAge') according to the fill-grade of the newSpace.
|
93
|
212 |
|
|
213 |
To reclaim oldspace, the system uses three algorithms: mark&sweep, a copying
|
|
214 |
(and compressing) baker-type collector and an incremental mark&sweep.
|
|
215 |
|
|
216 |
The mark&sweep runs whenever the oldspace becomes full, putting dead objects
|
|
217 |
onto a free list. If a memory request cannot be served from this freelist,
|
|
218 |
and the total size of objects on the freelist exceeds a threshold, the system
|
|
219 |
will compress the oldspace to make the free-space into one big area.
|
207
|
220 |
This compress is done by copying all live objects into a newly allocated
|
|
221 |
area, and freeing the previous memory afterwards (baker collector).
|
93
|
222 |
Since a compressing oldspace collect leads to a noticable pause of the system,
|
|
223 |
the memory manager tries hard to avoid oldspace compression.
|
207
|
224 |
(actually, if enough real memory is available to hold both spaces in physical
|
|
225 |
memory, the compress is pretty fast).
|
202
|
226 |
|
93
|
227 |
The incremental mark&sweep runs in the background, whenever the system is idle
|
|
228 |
(see ProcessorSceduler>>waitForEventOrTimeout). Like the normal mark&sweep,
|
|
229 |
this incremental collector follows object references and marks reachable objects
|
|
230 |
on its way. This is done 'a few objects-at-a-time', to not disrupt the system
|
270
|
231 |
noticable.
|
|
232 |
Incremental collection is controlled by the variable 'IncrementalGCLimit':
|
|
233 |
the ProcessorScheduler will perform incremental GC steps at idle time,
|
|
234 |
if the total space allocated since the last full collect exceeds
|
|
235 |
IncrementalGCLimit.
|
|
236 |
The default is set in ObjectMemory>>initialize and can be changed in your
|
|
237 |
startup 'smalltalk.rc'-file. Setting it to nil will turn incremental GC off.
|
|
238 |
|
|
239 |
For example, setting IncrementalGCLimit to 500000 will start the background collector
|
207
|
240 |
whenever 500k bytes have been allocated - usually very seldom. Setting it to some
|
|
241 |
small number (say 10000) will have it run very often.
|
213
|
242 |
Setting it to a small number (i.e. running it often) should not hurt the
|
|
243 |
performance of your other smalltalk processes, since the IGC only runs at
|
|
244 |
idle times. (there are some delays in event processing, since the IGC's steps
|
270
|
245 |
may take some XX ms.)
|
|
246 |
However, if you are not alone on your machine (i.e. a timesharing system) or
|
|
247 |
you have other Unix processes to run, you should not run the IGC too often,
|
|
248 |
since it may hurt other users.
|
93
|
249 |
|
216
|
250 |
Since this collector only runs at idle times, even a low priority background
|
|
251 |
process will prevent it from doing its work. You may want to start a somewhat
|
207
|
252 |
higher priority background collect (say at prio 4), which also preempts these
|
|
253 |
background processes. (see ObjectMemory>>startBackgroundCollectorAt:).
|
202
|
254 |
|
216
|
255 |
Beginning with 2.10.4, a third space, called fixSpace has been added.
|
|
256 |
Objects in this space are never moved or garbage collected.
|
|
257 |
This space is currently used for (some) symbols only, but additional constant
|
229
|
258 |
objects may be put into it in the future (true, false, some basic classes etc.).
|
|
259 |
|
|
260 |
A plan for 2.11 is to offer an arbitrary number of spaces, which can be
|
|
261 |
attached and detached at runtime. This will allow easy share of object
|
|
262 |
with remote systems and separating objects into a per application/package
|
|
263 |
space. (be prepared for changes in the future and make your application
|
|
264 |
independ of the VM internals)
|
216
|
265 |
|
202
|
266 |
hints & tricks:
|
|
267 |
|
213
|
268 |
normally, there is no need to call for an explicit garbage collection, or
|
216
|
269 |
modify the default parameters.
|
213
|
270 |
The memory system should adapt reasonable and provide good performance
|
202
|
271 |
for a wide range of allocation patterns (see Example3 below for an exception).
|
|
272 |
|
207
|
273 |
However, there may be situations, in which hints and/or explicit
|
202
|
274 |
control over allocation can speedup your programs; but please:
|
93
|
275 |
|
207
|
276 |
- if you think you have to play around with the memory policies,
|
202
|
277 |
first check your program - you may find useless allocations
|
|
278 |
or bad uses of collections. A typical error that is made is to
|
|
279 |
create large collections using the #, (comma) concatenation method,
|
|
280 |
which shows square behavior, since it allocates many, many temporary
|
213
|
281 |
collections. Also, watch out for #copyWith:, #add: etc.
|
202
|
282 |
All of these create a new collection. Remember, that most collections
|
|
283 |
offer methods to preallocate some space; for example, 'Set new:' creates
|
229
|
284 |
an empty set, but preallocates space to avoid resizing over and over.
|
|
285 |
|
216
|
286 |
An especially bad performace dog is to use #add: on fix-size collection
|
|
287 |
objects (such as Strings or Arrays), since in addition to allocating
|
229
|
288 |
lots of garbage, a #become: operation is required for EACH element
|
216
|
289 |
added. NEVER use Arrays for growing/shrinking data - use OrderedCollection
|
229
|
290 |
instead. (if you really need an array, use asArray afterwards)
|
202
|
291 |
|
207
|
292 |
- if you are going to allocate huge data structures, think about
|
|
293 |
optimizing space. For example, if you allocate a million instances of
|
213
|
294 |
some object, each added instance variable makes up 4Mb of additional
|
|
295 |
memory need.
|
207
|
296 |
Also, for Byte-valued, Integer-valued and Float like objects, special
|
|
297 |
collections are provided, which store their values directly inside (instead
|
|
298 |
of a reference to the object). A FloatArray consisting of 1 million floats
|
213
|
299 |
requires about 4mb of memory, while an Array of Floats requires 4mb for the
|
|
300 |
references to the floats, PLUS 20Mb for the floats themself.
|
93
|
301 |
|
207
|
302 |
- check if you really need fast access to all of these objects; you may
|
|
303 |
try to only keep some subset in memory, and use binary storage or
|
|
304 |
(if this is too slow) optimized store/retrieve methods and keep the bigger
|
229
|
305 |
part in a file.
|
|
306 |
(How about a DiskArray class, which does this transparent ?
|
|
307 |
See the FileText class for some ideas and something to start with ...)
|
207
|
308 |
|
|
309 |
|
|
310 |
Hint / Example 1:
|
93
|
311 |
you are about to allocate a huge data structure, which is known to
|
|
312 |
survive long. In this case, it is better to have these objects move into the
|
|
313 |
oldspace sooner, to avoid the copying overhead during scavenges.
|
|
314 |
|
|
315 |
To do this, you can call ObjectMemory>>tenure after allocation, which
|
|
316 |
forces all new-objects immediately into the oldspace.
|
|
317 |
Make certain, that not to many (ideally no) short-living objects are in the
|
|
318 |
newspace when doing this.
|
|
319 |
|
|
320 |
Another alternative is to tell the system that all allocation should be
|
|
321 |
done directly in the oldspace. This completely avoids the scavenging overhead
|
|
322 |
for these objects. To do so, use ObjectMemory>>turnGarbageCollectorOff
|
|
323 |
before the allocation, and ObjectMemory>>turnGarbageCollectorOn afterwards.
|
202
|
324 |
Keep in mind, that do-loops may allocate block-objects and other temporaries,
|
178
|
325 |
so there is a danger of making things worse due to having all those temporaries
|
93
|
326 |
in the oldspace afterwards. (which is not a fatal situation, but will
|
178
|
327 |
force the system to do an oldspace collect earlier, which may not be your
|
|
328 |
intention).
|
93
|
329 |
|
202
|
330 |
|
207
|
331 |
Hint / Example 2:
|
93
|
332 |
you know in advance, that a certain (big) amount of memory will be needed.
|
|
333 |
For example, the fileBrowser wants to show a huge file in its text-view.
|
133
|
334 |
In this case, it is better to tell the memory system in advance, how much
|
93
|
335 |
memory will be needed, since otherwise many compresses and reallocations will
|
133
|
336 |
occur (the memory system will allocate additional memory in chunks of smaller
|
|
337 |
256k pieces, if a compress failes. Thus, if you are going to allocate (say) 1Mb of
|
|
338 |
strings, it will perform 5 compressing GC's).
|
|
339 |
|
202
|
340 |
This is done using ObjectMemory>>moreOldSpace: or ObjectMemory announceSpaceNeed:.
|
|
341 |
In the above example, you would do 'ObjectMemory announceSpaceNeed:500000', which
|
133
|
342 |
avoids those annoying 5 compressing GC's.
|
207
|
343 |
BTW: if you have other smalltalk processes (threads) running which should not be
|
|
344 |
paused if possible, it is better to use #announceSpaceNeed. This tries to avoid
|
|
345 |
pausing in other processes and sometimes succeeds, while moreOldSpace will always
|
|
346 |
block the whole system for a while. However, there is no 'no-pause' guarantee.
|
133
|
347 |
|
|
348 |
The amount of automatic increase (in case the oldSpace becomes full) is 256k by
|
|
349 |
default. This number can be changed with ObjectMemory>>oldSpaceIncrement:.
|
|
350 |
|
207
|
351 |
|
|
352 |
Hint / Example3:
|
178
|
353 |
There are rare cases, when an explicit GC makes a difference: since
|
|
354 |
object finalization is done at GC time, objects which keep operatingSystem
|
|
355 |
resources may be finalized late. This is normally no problem, except if
|
|
356 |
the system is running out of resources. For example, allocating new colors
|
|
357 |
may fail if many colors have already been allocated in the past - even
|
|
358 |
though these colors are actually free. The Depth8Image calls for an
|
|
359 |
explicit GC, whenever it fails to allocate a color for a bitmap, to force
|
|
360 |
finalization of free, but not yet finalized colors.
|
|
361 |
|
207
|
362 |
|
|
363 |
Hint 4:
|
|
364 |
If you run in too small of physical memory, the incremental GC may have a
|
|
365 |
bad effect on your working set: since it touches pages (which may otherwise
|
|
366 |
not be needed at the moment, the operating system is forced to steal other
|
|
367 |
(possibly more useful) pages from your set of incore pages.
|
|
368 |
You may get better performance, if you turn off the incremental GC while
|
|
369 |
processing a big data structure.
|
|
370 |
|
|
371 |
|
229
|
372 |
Warning: many of the methods found here are not standard and may not even be available in
|
133
|
373 |
future versions of ST/X. Use them only in very special situations or experiments.
|
229
|
374 |
|
207
|
375 |
Let me know about additional special features you think are useful, and about
|
|
376 |
special features you are using - this provides the feedback required to decide
|
|
377 |
which methods are to be removed or kept or enhanced in future versions.
|
93
|
378 |
"
|
2
|
379 |
! !
|
|
380 |
|
|
381 |
!ObjectMemory class methodsFor:'initialization'!
|
|
382 |
|
|
383 |
initialize
|
178
|
384 |
"initialize the class"
|
|
385 |
|
2
|
386 |
AllocationFailureSignal isNil ifTrue:[
|
159
|
387 |
Object initialize.
|
|
388 |
|
|
389 |
AllocationFailureSignal := Object errorSignal newSignalMayProceed:true.
|
|
390 |
AllocationFailureSignal nameClass:self message:#allocationFailureSignal.
|
|
391 |
AllocationFailureSignal notifierString:'allocation failure'.
|
178
|
392 |
|
|
393 |
LowSpaceSemaphore := Semaphore new.
|
2
|
394 |
].
|
|
395 |
IncrementalGCLimit := 500000.
|
270
|
396 |
FreeSpaceGCLimit := nil.
|
13
|
397 |
MemoryInterruptHandler := self
|
2
|
398 |
! !
|
|
399 |
|
|
400 |
!ObjectMemory class methodsFor:'signal access'!
|
|
401 |
|
|
402 |
allocationFailureSignal
|
13
|
403 |
"return the signal raised when an object allocation failed"
|
|
404 |
|
2
|
405 |
^ AllocationFailureSignal
|
|
406 |
! !
|
1
|
407 |
|
178
|
408 |
!ObjectMemory class methodsFor:'semaphore access'!
|
|
409 |
|
|
410 |
lowSpaceSemaphore
|
|
411 |
"return the semaphore that is signalled when the system detects a
|
|
412 |
low space condition. Usually, some time after this, an allocationFailure
|
|
413 |
will happen. You can have a cleanup process sitting in that semaphore and
|
|
414 |
start to release object."
|
|
415 |
|
|
416 |
^ LowSpaceSemaphore
|
|
417 |
! !
|
|
418 |
|
10
|
419 |
!ObjectMemory class methodsFor:'dependents access'!
|
|
420 |
|
|
421 |
dependents
|
|
422 |
"return the colleciton of my dependents"
|
|
423 |
|
|
424 |
^ Dependents
|
|
425 |
!
|
|
426 |
|
|
427 |
dependents:aCollection
|
|
428 |
"set the dependents collection"
|
|
429 |
|
|
430 |
Dependents := aCollection
|
282
|
431 |
!
|
|
432 |
|
|
433 |
dependentsDo:aBlock
|
|
434 |
"evaluate aBlock for all of my dependents.
|
|
435 |
Since this is performed at startup time (under the scheduler),
|
|
436 |
this is redefined here to catch abort signals.
|
|
437 |
Thus, if any error occurs in a #returnFromSnapshot,
|
|
438 |
the user can press abort to continue."
|
|
439 |
|
|
440 |
|deps|
|
|
441 |
|
|
442 |
deps := Dependents.
|
|
443 |
deps notNil ifTrue:[
|
|
444 |
deps do:[:each |
|
|
445 |
AbortSignal handle:[:ex |
|
|
446 |
ex return
|
|
447 |
] do:[
|
|
448 |
aBlock value:each
|
|
449 |
]
|
|
450 |
]
|
|
451 |
]
|
10
|
452 |
! !
|
|
453 |
|
1
|
454 |
!ObjectMemory class methodsFor:'cache management'!
|
|
455 |
|
|
456 |
flushInlineCachesForClass:aClass
|
93
|
457 |
"flush inlinecaches for calls to aClass."
|
1
|
458 |
|
|
459 |
%{ /* NOCONTEXT */
|
|
460 |
__flushInlineCachesFor(aClass);
|
|
461 |
%}
|
|
462 |
!
|
|
463 |
|
|
464 |
flushInlineCachesWithArgs:nargs
|
|
465 |
"flush inlinecaches for calls with nargs arguments"
|
|
466 |
|
|
467 |
%{ /* NOCONTEXT */
|
|
468 |
__flushInlineCaches(_intVal(nargs));
|
|
469 |
%}
|
|
470 |
!
|
|
471 |
|
|
472 |
flushInlineCachesFor:aClass withArgs:nargs
|
|
473 |
"flush inlinecaches for calls to aClass with nargs arguments"
|
|
474 |
|
|
475 |
%{ /* NOCONTEXT */
|
|
476 |
__flushInlineCachesForAndNargs(aClass, _intVal(nargs));
|
|
477 |
%}
|
|
478 |
!
|
|
479 |
|
|
480 |
flushInlineCaches
|
|
481 |
"flush all inlinecaches"
|
|
482 |
|
|
483 |
%{ /* NOCONTEXT */
|
|
484 |
__flushAllInlineCaches();
|
|
485 |
%}
|
|
486 |
!
|
|
487 |
|
|
488 |
flushMethodCacheFor:aClass
|
|
489 |
"flush the method cache for sends to aClass"
|
|
490 |
|
|
491 |
%{ /* NOCONTEXT */
|
|
492 |
__flushMethodCacheFor(aClass);
|
|
493 |
%}
|
|
494 |
!
|
|
495 |
|
|
496 |
flushMethodCache
|
|
497 |
"flush the method cache"
|
|
498 |
|
|
499 |
%{ /* NOCONTEXT */
|
|
500 |
__flushMethodCache();
|
|
501 |
%}
|
|
502 |
!
|
|
503 |
|
2
|
504 |
flushCachesFor:aClass
|
|
505 |
"flush method and inline caches for aClass"
|
|
506 |
|
|
507 |
%{ /* NOCONTEXT */
|
|
508 |
__flushMethodCacheFor(aClass);
|
|
509 |
__flushInlineCachesFor(aClass);
|
|
510 |
%}
|
|
511 |
!
|
|
512 |
|
1
|
513 |
flushCaches
|
2
|
514 |
"flush method and inline caches for all classes"
|
1
|
515 |
|
|
516 |
%{ /* NOCONTEXT */
|
|
517 |
__flushMethodCache();
|
|
518 |
__flushAllInlineCaches();
|
|
519 |
%}
|
|
520 |
! !
|
|
521 |
|
216
|
522 |
!ObjectMemory class methodsFor:'enumerating'!
|
1
|
523 |
|
|
524 |
allObjectsDo:aBlock
|
85
|
525 |
"evaluate the argument, aBlock for all objects in the system.
|
|
526 |
There is one caveat: if a compressing oldSpace collect
|
|
527 |
occurs while looping over the objects, the loop cannot be
|
|
528 |
continued (for some internal reasons). In this case, false
|
|
529 |
is returned."
|
2
|
530 |
|
|
531 |
|work|
|
22
|
532 |
|
|
533 |
%{ /* NOREGISTER - work may not be placed into a register here */
|
2
|
534 |
nonTenuringScavenge(__context);
|
|
535 |
/*
|
|
536 |
* allObjectsDo needs a temporary to hold newSpace objects
|
|
537 |
*/
|
85
|
538 |
if (__allObjectsDo(&aBlock, &work COMMA_CON) < 0) {
|
159
|
539 |
RETURN (false);
|
85
|
540 |
}
|
|
541 |
%}.
|
|
542 |
^ true
|
2
|
543 |
!
|
|
544 |
|
|
545 |
allOldObjectsDo:aBlock
|
|
546 |
"evaluate the argument, aBlock for all old objects in the system.
|
|
547 |
For debugging and tests only - do not use"
|
|
548 |
%{
|
85
|
549 |
if (__allObjectsDo(&aBlock, (OBJ *)0 COMMA_CON) < 0) {
|
159
|
550 |
RETURN (false);
|
85
|
551 |
}
|
|
552 |
%}.
|
|
553 |
^ true
|
1
|
554 |
! !
|
|
555 |
|
229
|
556 |
!ObjectMemory class methodsFor:'interrupt handler access'!
|
2
|
557 |
|
|
558 |
internalErrorHandler
|
13
|
559 |
"return the handler for ST/X internal errors.
|
|
560 |
An internal error is reported for example when a methods
|
|
561 |
bytecode is not a ByteArray, the selector table is not an Array
|
93
|
562 |
etc.
|
|
563 |
Those should not occur in normal circumstances."
|
13
|
564 |
|
2
|
565 |
^ InternalErrorHandler
|
|
566 |
!
|
|
567 |
|
|
568 |
userInterruptHandler
|
13
|
569 |
"return the handler for CNTL-C interrupt handling"
|
|
570 |
|
2
|
571 |
^ UserInterruptHandler
|
|
572 |
!
|
|
573 |
|
|
574 |
userInterruptHandler:aHandler
|
13
|
575 |
"set the handler for CNTL-C interrupt handling"
|
|
576 |
|
2
|
577 |
UserInterruptHandler := aHandler
|
|
578 |
!
|
|
579 |
|
|
580 |
timerInterruptHandler
|
13
|
581 |
"return the handler for timer interrupts"
|
|
582 |
|
2
|
583 |
^ TimerInterruptHandler
|
|
584 |
!
|
|
585 |
|
10
|
586 |
timerInterruptHandler:aHandler
|
13
|
587 |
"set the handler for timer interrupts"
|
|
588 |
|
10
|
589 |
TimerInterruptHandler := aHandler
|
|
590 |
!
|
|
591 |
|
2
|
592 |
spyInterruptHandler
|
13
|
593 |
"return the handler for spy-timer interrupts"
|
|
594 |
|
2
|
595 |
^ SpyInterruptHandler
|
|
596 |
!
|
|
597 |
|
|
598 |
spyInterruptHandler:aHandler
|
13
|
599 |
"set the handler for spy-timer interrupts"
|
|
600 |
|
2
|
601 |
SpyInterruptHandler := aHandler
|
|
602 |
!
|
|
603 |
|
|
604 |
stepInterruptHandler
|
13
|
605 |
"return the handler for single step interrupts"
|
|
606 |
|
2
|
607 |
^ StepInterruptHandler
|
|
608 |
!
|
|
609 |
|
|
610 |
stepInterruptHandler:aHandler
|
13
|
611 |
"set the handler for single step interrupts"
|
|
612 |
|
2
|
613 |
StepInterruptHandler := aHandler
|
|
614 |
!
|
|
615 |
|
|
616 |
exceptionInterruptHandler
|
13
|
617 |
"return the handler for floating point exception interrupts"
|
|
618 |
|
2
|
619 |
^ ExceptionInterruptHandler
|
|
620 |
!
|
|
621 |
|
|
622 |
errorInterruptHandler
|
13
|
623 |
"return the handler for display error interrupts"
|
|
624 |
|
2
|
625 |
^ ErrorInterruptHandler
|
|
626 |
!
|
|
627 |
|
|
628 |
errorInterruptHandler:aHandler
|
13
|
629 |
"set the handler for display error interrupts"
|
|
630 |
|
2
|
631 |
ErrorInterruptHandler := aHandler
|
|
632 |
!
|
|
633 |
|
13
|
634 |
signalInterruptHandler
|
|
635 |
"return the handler for UNIX-signal interrupts"
|
2
|
636 |
|
|
637 |
^ SignalInterruptHandler
|
|
638 |
!
|
|
639 |
|
13
|
640 |
signalInterruptHandler:aHandler
|
|
641 |
"set the handler for UNIX-signal interrupts"
|
|
642 |
|
|
643 |
SignalInterruptHandler := aHandler
|
|
644 |
!
|
|
645 |
|
2
|
646 |
childSignalInterruptHandler
|
13
|
647 |
"return the handler for UNIX-death-of-a-childprocess-signal interrupts"
|
|
648 |
|
2
|
649 |
^ ChildSignalInterruptHandler
|
|
650 |
!
|
|
651 |
|
|
652 |
disposeInterruptHandler
|
13
|
653 |
"return the handler for object disposal interrupts"
|
|
654 |
|
2
|
655 |
^ DisposeInterruptHandler
|
|
656 |
!
|
|
657 |
|
|
658 |
disposeInterruptHandler:aHandler
|
13
|
659 |
"set the handler for object disposal interrupts"
|
|
660 |
|
2
|
661 |
DisposeInterruptHandler := aHandler
|
|
662 |
!
|
|
663 |
|
|
664 |
recursionInterruptHandler
|
13
|
665 |
"return the handler for recursion/stack overflow interrupts"
|
|
666 |
|
2
|
667 |
^ RecursionInterruptHandler
|
|
668 |
!
|
|
669 |
|
13
|
670 |
recursionInterruptHandler:aHandler
|
|
671 |
"set the handler for recursion/stack overflow interrupts"
|
|
672 |
|
|
673 |
RecursionInterruptHandler := aHandler
|
|
674 |
!
|
|
675 |
|
2
|
676 |
ioInterruptHandler
|
13
|
677 |
"return the handler for I/O available signal interrupts (SIGIO/SIGPOLL)"
|
|
678 |
|
2
|
679 |
^ IOInterruptHandler
|
|
680 |
!
|
|
681 |
|
|
682 |
ioInterruptHandler:aHandler
|
13
|
683 |
"set the handler for I/O available signal interrupts (SIGIO/SIGPOLL)"
|
|
684 |
|
2
|
685 |
IOInterruptHandler := aHandler
|
85
|
686 |
!
|
|
687 |
|
|
688 |
customInterruptHandler
|
|
689 |
"return the handler for custom interrupts"
|
|
690 |
|
|
691 |
^ CustomInterruptHandler
|
|
692 |
!
|
|
693 |
|
|
694 |
customInterruptHandler:aHandler
|
|
695 |
"set the handler for custom interrupts"
|
|
696 |
|
|
697 |
CustomInterruptHandler := aHandler
|
2
|
698 |
! !
|
|
699 |
|
1
|
700 |
!ObjectMemory class methodsFor:'queries'!
|
|
701 |
|
13
|
702 |
newSpaceSize
|
|
703 |
"return the total size of the new space - this is usually fix"
|
|
704 |
|
|
705 |
%{ /* NOCONTEXT */
|
133
|
706 |
extern unsigned __newSpaceSize();
|
|
707 |
|
13
|
708 |
RETURN ( _MKSMALLINT(__newSpaceSize()) );
|
|
709 |
%}
|
93
|
710 |
"
|
|
711 |
ObjectMemory newSpaceSize
|
|
712 |
"
|
13
|
713 |
!
|
|
714 |
|
|
715 |
oldSpaceSize
|
|
716 |
"return the total size of the old space. - may grow slowly"
|
|
717 |
|
|
718 |
%{ /* NOCONTEXT */
|
133
|
719 |
extern unsigned __oldSpaceSize();
|
|
720 |
|
13
|
721 |
RETURN ( _MKSMALLINT(__oldSpaceSize()) );
|
|
722 |
%}
|
93
|
723 |
"
|
|
724 |
ObjectMemory oldSpaceSize
|
|
725 |
"
|
13
|
726 |
!
|
|
727 |
|
216
|
728 |
fixSpaceSize
|
|
729 |
"return the total size of the fix space."
|
|
730 |
|
|
731 |
%{ /* NOCONTEXT */
|
|
732 |
extern unsigned __fixSpaceSize();
|
|
733 |
|
|
734 |
RETURN ( _MKSMALLINT(__fixSpaceSize()) );
|
|
735 |
%}
|
|
736 |
"
|
|
737 |
ObjectMemory fixSpaceSize
|
|
738 |
"
|
|
739 |
!
|
|
740 |
|
1
|
741 |
newSpaceUsed
|
10
|
742 |
"return the number of bytes allocated for new objects.
|
|
743 |
The returned value is usually obsolete as soon as you do
|
13
|
744 |
something with it ..."
|
1
|
745 |
|
|
746 |
%{ /* NOCONTEXT */
|
133
|
747 |
extern unsigned __newSpaceUsed();
|
|
748 |
|
1
|
749 |
RETURN ( _MKSMALLINT(__newSpaceUsed()) );
|
|
750 |
%}
|
93
|
751 |
"
|
202
|
752 |
ObjectMemory newSpaceUsed
|
93
|
753 |
"
|
1
|
754 |
!
|
|
755 |
|
|
756 |
oldSpaceUsed
|
10
|
757 |
"return the number of bytes allocated for old objects.
|
|
758 |
(This includes the free lists)"
|
1
|
759 |
|
|
760 |
%{ /* NOCONTEXT */
|
133
|
761 |
extern unsigned __oldSpaceUsed();
|
|
762 |
|
1
|
763 |
RETURN ( _MKSMALLINT(__oldSpaceUsed()) );
|
|
764 |
%}
|
93
|
765 |
"
|
202
|
766 |
ObjectMemory oldSpaceUsed
|
93
|
767 |
"
|
1
|
768 |
!
|
|
769 |
|
216
|
770 |
fixSpaceUsed
|
|
771 |
"return the number of bytes allocated for old objects in fix space."
|
|
772 |
|
|
773 |
%{ /* NOCONTEXT */
|
|
774 |
extern unsigned __fixSpaceUsed();
|
|
775 |
|
|
776 |
RETURN ( _MKSMALLINT(__fixSpaceUsed()) );
|
|
777 |
%}
|
|
778 |
"
|
|
779 |
ObjectMemory fixSpaceUsed
|
|
780 |
"
|
|
781 |
!
|
|
782 |
|
270
|
783 |
freeSpace
|
|
784 |
"return the number of bytes in the compact free area.
|
|
785 |
(oldSpaceUsed + freeSpaceSize = oldSpaceSize)"
|
|
786 |
|
|
787 |
%{ /* NOCONTEXT */
|
|
788 |
extern unsigned __oldSpaceSize(), __oldSpaceUsed();
|
|
789 |
|
|
790 |
RETURN ( _MKSMALLINT(__oldSpaceSize() - __oldSpaceUsed()) );
|
|
791 |
%}
|
|
792 |
"
|
|
793 |
ObjectMemory freeSpace
|
|
794 |
"
|
|
795 |
!
|
|
796 |
|
133
|
797 |
freeListSpace
|
10
|
798 |
"return the number of bytes in the free lists.
|
|
799 |
(which is included in oldSpaceUsed)"
|
|
800 |
|
|
801 |
%{ /* NOCONTEXT */
|
133
|
802 |
extern unsigned __freeListSpace();
|
|
803 |
|
|
804 |
RETURN ( _MKSMALLINT(__freeListSpace()) );
|
10
|
805 |
%}
|
93
|
806 |
"
|
133
|
807 |
ObjectMemory freeListSpace
|
93
|
808 |
"
|
10
|
809 |
!
|
|
810 |
|
1
|
811 |
bytesUsed
|
|
812 |
"return the number of bytes allocated for objects -
|
178
|
813 |
this number is not exact, since some objects may already be dead
|
|
814 |
(i.e. not yet reclaimed by the garbage collector).
|
|
815 |
If you need the exact number, you have to loop over all
|
|
816 |
objects and ask for the bytesize using ObjectMemory>>sizeOf:."
|
1
|
817 |
|
|
818 |
%{ /* NOCONTEXT */
|
133
|
819 |
extern unsigned __oldSpaceUsed(), __newSpaceUsed(), __freeListSpace();
|
|
820 |
|
|
821 |
RETURN ( _MKSMALLINT(__oldSpaceUsed() + __newSpaceUsed() - __freeListSpace()) );
|
1
|
822 |
%}
|
93
|
823 |
"
|
202
|
824 |
ObjectMemory bytesUsed
|
93
|
825 |
"
|
1
|
826 |
!
|
|
827 |
|
2
|
828 |
oldSpaceAllocatedSinceLastGC
|
|
829 |
"return the number of bytes allocated for old objects since the
|
178
|
830 |
last oldspace garbage collect occured. This information is used
|
|
831 |
by ProcessorScheduler to decide when to start the incremental
|
|
832 |
background GC."
|
2
|
833 |
|
|
834 |
%{ /* NOCONTEXT */
|
133
|
835 |
extern unsigned __oldSpaceAllocatedSinceLastGC();
|
|
836 |
|
2
|
837 |
RETURN ( _MKSMALLINT(__oldSpaceAllocatedSinceLastGC()) );
|
|
838 |
%}
|
93
|
839 |
"
|
202
|
840 |
ObjectMemory oldSpaceAllocatedSinceLastGC
|
93
|
841 |
"
|
2
|
842 |
!
|
|
843 |
|
178
|
844 |
tenureAge
|
|
845 |
"return the current tenure age - thats the number of times
|
|
846 |
an object has to survive scavenges to be moved into oldSpace.
|
202
|
847 |
For statistic/debugging only - this method may vanish"
|
178
|
848 |
|
|
849 |
%{ /* NOCONTEXT */
|
|
850 |
extern unsigned __tenureAge();
|
|
851 |
|
|
852 |
RETURN ( _MKSMALLINT(__tenureAge()) );
|
|
853 |
%}
|
|
854 |
!
|
|
855 |
|
194
|
856 |
lastScavangeReclamation
|
|
857 |
"returns the number of bytes replacimed by the last scavenge.
|
|
858 |
For statistic only - this may vanish."
|
|
859 |
|
|
860 |
%{ /* NOCONTEXT */
|
|
861 |
extern int __newSpaceReclaimed();
|
|
862 |
|
|
863 |
RETURN ( _MKSMALLINT(__newSpaceReclaimed()) );
|
|
864 |
%}
|
202
|
865 |
"percentage of reclaimed objects is returned by:
|
|
866 |
|
194
|
867 |
((ObjectMemory lastScavangeReclamation)
|
202
|
868 |
/ (ObjectMemory newSpaceSize)) * 100.0
|
194
|
869 |
"
|
|
870 |
!
|
|
871 |
|
178
|
872 |
runsSingleOldSpace
|
|
873 |
"return true, if the system runs in a single oldSpace or
|
|
874 |
false, if it has given up baker-collection. The memory
|
|
875 |
system will always drop the second semispace when running out of
|
|
876 |
virtual memory, or the baker-limit is reached.
|
202
|
877 |
OBSOLETE:
|
|
878 |
the system may now decide at any time to switch between
|
|
879 |
single and double-space algorithms, depending on the overall memory
|
|
880 |
size. You will now almost always get false as result, since the
|
|
881 |
second semispaces are only allocated when needed, and released
|
|
882 |
afterwards.
|
|
883 |
"
|
178
|
884 |
|
|
885 |
%{ /* NOCONTEXT */
|
|
886 |
extern char *collectedOldStartPtr;
|
|
887 |
|
|
888 |
RETURN ( ((collectedOldStartPtr == (char *)0) ? true : false) );
|
|
889 |
%}
|
202
|
890 |
"
|
|
891 |
ObjectMemory runsSingleOldSpace
|
|
892 |
"
|
178
|
893 |
!
|
|
894 |
|
|
895 |
incrementalGCPhase
|
|
896 |
"returns the internal state of the incremental GC.
|
|
897 |
The meaning of those numbers is a secret :-).
|
|
898 |
This is for debugging and monitoring only - and may vanish"
|
|
899 |
|
|
900 |
%{ /* NOCONTEXT */
|
|
901 |
extern int __incrGCphase();
|
|
902 |
|
|
903 |
RETURN (_MKSMALLINT(__incrGCphase()));
|
|
904 |
%}
|
|
905 |
!
|
|
906 |
|
270
|
907 |
scavengeCount
|
|
908 |
"return the number of scavenges that occurred since startup"
|
|
909 |
|
|
910 |
%{ /* NOCONTEXT */
|
|
911 |
extern int __scavengeCount();
|
|
912 |
|
|
913 |
RETURN (_MKSMALLINT(__scavengeCount()));
|
|
914 |
%}
|
|
915 |
"
|
|
916 |
ObjectMemory scavengeCount
|
|
917 |
"
|
|
918 |
!
|
|
919 |
|
|
920 |
markAndSweepCount
|
|
921 |
"return the number of mark&sweep collects that occurred since startup"
|
|
922 |
|
|
923 |
%{ /* NOCONTEXT */
|
|
924 |
extern int __markAndSweepCount();
|
|
925 |
|
|
926 |
RETURN (_MKSMALLINT(__markAndSweepCount()));
|
|
927 |
%}
|
|
928 |
"
|
|
929 |
ObjectMemory markAndSweepCount
|
|
930 |
"
|
|
931 |
!
|
|
932 |
|
|
933 |
garbageCollectCount
|
|
934 |
"return the number of compressing collects that occurred since startup"
|
|
935 |
|
|
936 |
%{ /* NOCONTEXT */
|
|
937 |
extern int __garbageCollectCount();
|
|
938 |
|
|
939 |
RETURN (_MKSMALLINT(__garbageCollectCount()));
|
|
940 |
%}
|
|
941 |
"
|
|
942 |
ObjectMemory garbageCollectCount
|
|
943 |
"
|
|
944 |
!
|
|
945 |
|
1
|
946 |
numberOfObjects
|
13
|
947 |
"return the number of objects in the system."
|
1
|
948 |
|
|
949 |
|tally|
|
|
950 |
|
|
951 |
tally := 0.
|
|
952 |
self allObjectsDo:[:obj | tally := tally + 1].
|
|
953 |
^ tally
|
|
954 |
|
93
|
955 |
"
|
202
|
956 |
ObjectMemory numberOfObjects
|
93
|
957 |
"
|
1
|
958 |
!
|
|
959 |
|
|
960 |
printReferences:anObject
|
178
|
961 |
"for debugging: print referents to anObject.
|
202
|
962 |
WARNING:
|
|
963 |
this method will vanish;
|
|
964 |
use ObjectMemory>>whoReferences: or anObject>>allOwners."
|
1
|
965 |
|
|
966 |
%{
|
|
967 |
_printRefChain(__context, anObject);
|
|
968 |
%}
|
|
969 |
!
|
|
970 |
|
202
|
971 |
collectObjectsWhich:aBlock
|
|
972 |
"helper for the whoReferences queries. Returns a collection
|
|
973 |
of objects for which aBlock returns true."
|
1
|
974 |
|
|
975 |
|aCollection|
|
|
976 |
|
|
977 |
aCollection := IdentitySet new.
|
|
978 |
self allObjectsDo:[:o |
|
202
|
979 |
(aBlock value:o) ifTrue:[
|
159
|
980 |
aCollection add:o
|
|
981 |
]
|
1
|
982 |
].
|
|
983 |
(aCollection size == 0) ifTrue:[
|
159
|
984 |
"actually this cannot happen - there is always one"
|
|
985 |
^ nil
|
1
|
986 |
].
|
|
987 |
^ aCollection
|
13
|
988 |
!
|
|
989 |
|
202
|
990 |
whoReferences:anObject
|
|
991 |
"return a collection of objects referencing the argument, anObject"
|
|
992 |
|
|
993 |
^ self collectObjectsWhich:[:o | o references:anObject]
|
|
994 |
|
|
995 |
"
|
|
996 |
(ObjectMemory whoReferences:Transcript) printNL
|
|
997 |
"
|
|
998 |
!
|
|
999 |
|
|
1000 |
whoReferencesInstancesOf:aClass
|
|
1001 |
"return a collection of objects refering to instances
|
|
1002 |
of the argument, aClass"
|
|
1003 |
|
|
1004 |
^ self collectObjectsWhich:[:o | o referencesInstanceOf:aClass]
|
|
1005 |
|
|
1006 |
"
|
|
1007 |
(ObjectMemory whoReferencesInstancesOf:SystemBrowser) printNL
|
|
1008 |
"
|
|
1009 |
!
|
|
1010 |
|
|
1011 |
whoReferencesDerivedInstancesOf:aClass
|
|
1012 |
"return a collection of objects refering to instances
|
|
1013 |
of the argument, aClass or a subclass of it."
|
|
1014 |
|
|
1015 |
^ self collectObjectsWhich:[:o | o referencesDerivedInstanceOf:aClass]
|
|
1016 |
|
|
1017 |
"
|
|
1018 |
(ObjectMemory whoReferencesDerivedInstancesOf:View) printNL
|
|
1019 |
"
|
|
1020 |
!
|
|
1021 |
|
13
|
1022 |
addressOf:anObject
|
|
1023 |
"return the core address of anObject as an integer
|
|
1024 |
- since objects may move around, the returned value is invalid after the
|
|
1025 |
next scavenge/collect.
|
|
1026 |
Use only for debugging."
|
|
1027 |
|
|
1028 |
%{ /* NOCONTEXT */
|
|
1029 |
|
|
1030 |
if (! _isNonNilObject(anObject)) {
|
159
|
1031 |
RETURN ( nil );
|
13
|
1032 |
}
|
|
1033 |
RETURN ( _MKSMALLINT( (int)anObject ) );
|
|
1034 |
%}
|
|
1035 |
"
|
|
1036 |
|p|
|
|
1037 |
p := Point new.
|
77
|
1038 |
(ObjectMemory addressOf:p) printNL.
|
13
|
1039 |
ObjectMemory scavenge.
|
77
|
1040 |
(ObjectMemory addressOf:p) printNL.
|
13
|
1041 |
"
|
|
1042 |
!
|
|
1043 |
|
159
|
1044 |
objectAt:anAddress
|
|
1045 |
"return whatever anAddress points to as object.
|
202
|
1046 |
BIG BIG DANGER ALERT:
|
|
1047 |
this method is only to be used for debugging
|
|
1048 |
ST/X itself - you can easily (and badly) crash the system.
|
159
|
1049 |
This method will be removed from the final shipping version"
|
|
1050 |
|
|
1051 |
|low high|
|
|
1052 |
|
|
1053 |
low := anAddress bitAnd:16rFFFF.
|
|
1054 |
high := (anAddress bitShift:16) bitAnd:16rFFFF.
|
|
1055 |
%{
|
253
|
1056 |
if (__bothSmallInteger(low, high)) {
|
159
|
1057 |
RETURN ((OBJ)((_intVal(high) << 16) | _intVal(low)));
|
|
1058 |
}
|
|
1059 |
%}
|
|
1060 |
!
|
|
1061 |
|
13
|
1062 |
sizeOf:anObject
|
|
1063 |
"return the size of anObject in bytes.
|
|
1064 |
Use only for debugging/memory monitoring."
|
|
1065 |
|
|
1066 |
%{ /* NOCONTEXT */
|
|
1067 |
|
|
1068 |
RETURN ( _isNonNilObject(anObject) ? _MKSMALLINT(_qSize(anObject)) : _MKSMALLINT(0) )
|
|
1069 |
%}
|
|
1070 |
"
|
|
1071 |
|hist big nw|
|
|
1072 |
|
|
1073 |
hist := Array new:100 withAll:0.
|
|
1074 |
big := 0.
|
|
1075 |
ObjectMemory allObjectsDo:[:o |
|
159
|
1076 |
nw := (ObjectMemory sizeOf:o) // 4 + 1.
|
|
1077 |
nw > 100 ifTrue:[
|
|
1078 |
big := big + 1
|
|
1079 |
] ifFalse:[
|
|
1080 |
hist at:nw put:(hist at:nw) + 1
|
|
1081 |
].
|
13
|
1082 |
].
|
77
|
1083 |
hist printNL.
|
|
1084 |
big printNL
|
13
|
1085 |
"
|
|
1086 |
!
|
|
1087 |
|
|
1088 |
spaceOf:anObject
|
|
1089 |
"return the memory space, in which anObject is.
|
|
1090 |
- since objects may move between spaces, returned value is invalid after the
|
|
1091 |
next scavenge/collect.
|
202
|
1092 |
For debugging only; Dont use this method; it may vanish."
|
13
|
1093 |
|
|
1094 |
%{ /* NOCONTEXT */
|
|
1095 |
|
|
1096 |
if (! _isNonNilObject(anObject)) {
|
159
|
1097 |
RETURN ( nil );
|
13
|
1098 |
}
|
|
1099 |
RETURN ( _MKSMALLINT( _qSpace(anObject) ) );
|
|
1100 |
%}
|
159
|
1101 |
!
|
|
1102 |
|
|
1103 |
flagsOf:anObject
|
202
|
1104 |
"For debugging only; Dont use this method; it may vanish."
|
|
1105 |
|
159
|
1106 |
%{ /* NOCONTEXT */
|
|
1107 |
|
|
1108 |
if (! _isNonNilObject(anObject)) {
|
|
1109 |
RETURN ( nil );
|
|
1110 |
}
|
|
1111 |
RETURN ( _MKSMALLINT( anObject->o_flags ) );
|
|
1112 |
%}
|
13
|
1113 |
"
|
202
|
1114 |
F_ISREMEMBERED 1 /* a new-space thing being refd by some oldSpace thing */
|
|
1115 |
F_ISFORWARDED 2 /* a forwarded object (you will never see this here) */
|
159
|
1116 |
F_DEREFERENCED 4 /* a collection after grow (not currently used) */
|
|
1117 |
F_ISONLIFOLIST 8 /* a non-lifo-context-referencing-obj already on list */
|
|
1118 |
F_MARK 16 /* mark bit for background collector */
|
77
|
1119 |
"
|
|
1120 |
!
|
|
1121 |
|
|
1122 |
ageOf:anObject
|
|
1123 |
"return the number of scavenges, an object has survived
|
202
|
1124 |
in new space. For old objects and living contexts, the returned number
|
|
1125 |
is invalid.
|
|
1126 |
For debugging only; Dont use this method; it may vanish."
|
77
|
1127 |
|
|
1128 |
%{ /* NOCONTEXT */
|
|
1129 |
|
|
1130 |
if (! _isNonNilObject(anObject)) {
|
159
|
1131 |
RETURN ( 0 );
|
77
|
1132 |
}
|
|
1133 |
RETURN ( _MKSMALLINT( _GET_AGE(anObject) ) );
|
|
1134 |
%}
|
|
1135 |
"
|
|
1136 |
|p|
|
|
1137 |
p := Point new.
|
|
1138 |
(ObjectMemory ageOf:p) printNL.
|
|
1139 |
ObjectMemory tenuringScavenge.
|
|
1140 |
(ObjectMemory spaceOf:p) printNL.
|
|
1141 |
ObjectMemory tenuringScavenge.
|
|
1142 |
(ObjectMemory spaceOf:p) printNL.
|
|
1143 |
ObjectMemory tenuringScavenge.
|
|
1144 |
(ObjectMemory spaceOf:p) printNL.
|
|
1145 |
ObjectMemory tenuringScavenge.
|
|
1146 |
(ObjectMemory spaceOf:p) printNL.
|
13
|
1147 |
"
|
1
|
1148 |
! !
|
|
1149 |
|
178
|
1150 |
!ObjectMemory class methodsFor:'garbage collection'!
|
1
|
1151 |
|
133
|
1152 |
scavenge
|
|
1153 |
"collect young objects, without aging (i.e. no tenure).
|
|
1154 |
Can be used to quickly get rid of shortly before allocated
|
|
1155 |
stuff. This is relatively fast (compared to oldspace collect).
|
|
1156 |
|
|
1157 |
An example where a non-tenuring scavenge makes sense is when
|
|
1158 |
allocating some OperatingSystem resource (a Color, File or View)
|
|
1159 |
and the OS runs out of resources. In this case, the scavenge may
|
|
1160 |
free some ST-objects and therefore (by signalling the WeakArrays
|
|
1161 |
or Registries) free the OS resources too.
|
|
1162 |
Of course, only recently allocated resources will be freed this
|
|
1163 |
way. If none was freed, a full collect will be needed."
|
|
1164 |
%{
|
|
1165 |
nonTenuringScavenge(__context);
|
|
1166 |
%}
|
|
1167 |
|
|
1168 |
"
|
|
1169 |
ObjectMemory scavenge
|
|
1170 |
"
|
|
1171 |
!
|
|
1172 |
|
|
1173 |
tenuringScavenge
|
|
1174 |
"collect newspace stuff, with aging (i.e. objects old enough
|
|
1175 |
will be moved into the oldSpace).
|
|
1176 |
Use this for debugging and testing only - the system performs
|
|
1177 |
this automatically when the newspace fills up.
|
|
1178 |
This is relatively fast (compared to oldspace collect)"
|
|
1179 |
%{
|
|
1180 |
scavenge(__context);
|
|
1181 |
%}
|
|
1182 |
|
|
1183 |
"
|
|
1184 |
ObjectMemory tenuringScavenge
|
|
1185 |
"
|
|
1186 |
!
|
|
1187 |
|
|
1188 |
tenure
|
|
1189 |
"force all living new stuff into old-space - effectively making
|
|
1190 |
all living young objects become old objects.
|
|
1191 |
This is relatively fast (compared to oldspace collect).
|
|
1192 |
|
|
1193 |
This method should only be used in very special situations:
|
|
1194 |
for example, when building up some long-living data structure
|
|
1195 |
in a time critical application.
|
194
|
1196 |
To do so, you have to do a scavenge followed by a tenure after the
|
133
|
1197 |
objects are created. Be careful, to not reference any other chunk-
|
|
1198 |
data when calling for a tenure (this will lead to lots of garbage in
|
|
1199 |
the oldspace).
|
|
1200 |
In normal situations, explicit tenures are not needed."
|
|
1201 |
%{
|
|
1202 |
tenure(__context);
|
|
1203 |
%}
|
|
1204 |
|
|
1205 |
"
|
|
1206 |
ObjectMemory tenure
|
|
1207 |
"
|
194
|
1208 |
"
|
|
1209 |
... build up long living objects ...
|
|
1210 |
ObjectMemory scavenge.
|
|
1211 |
ObjectMemory tenure
|
|
1212 |
... continue - objects created above are now in oldSpace ...
|
|
1213 |
"
|
133
|
1214 |
!
|
|
1215 |
|
1
|
1216 |
garbageCollect
|
178
|
1217 |
"search for and free garbage in the oldSpace (newSpace is cleaned automatically)
|
|
1218 |
performing a COMPRESSING garbage collect.
|
133
|
1219 |
This can take a long time - especially, if paging is involved
|
178
|
1220 |
(when no paging is involved, its faster than I thought :-).
|
202
|
1221 |
If no memory is available for the compress, or the system has been started with
|
|
1222 |
the -Msingle option, this does a non-COMPRESSING collect."
|
1
|
1223 |
%{
|
178
|
1224 |
if (! __garbageCollect(__context)) {
|
|
1225 |
markAndSweep(__context);
|
|
1226 |
}
|
1
|
1227 |
%}
|
|
1228 |
|
93
|
1229 |
"
|
|
1230 |
ObjectMemory garbageCollect
|
|
1231 |
"
|
1
|
1232 |
!
|
|
1233 |
|
93
|
1234 |
reclaimSymbols
|
|
1235 |
"reclaim unused symbols;
|
159
|
1236 |
Unused symbols are (currently) not reclaimed automatically,
|
93
|
1237 |
but only upon request with this method. It takes some time
|
|
1238 |
to do this ...
|
202
|
1239 |
Future versions may do this automatically, while garbage collecting."
|
93
|
1240 |
%{
|
|
1241 |
__reclaimSymbols(__context);
|
|
1242 |
%}
|
|
1243 |
"
|
|
1244 |
ObjectMemory reclaimSymbols
|
|
1245 |
"
|
|
1246 |
!
|
|
1247 |
|
1
|
1248 |
markAndSweep
|
133
|
1249 |
"mark/sweep garbage collector.
|
|
1250 |
perform a full mark&sweep collect.
|
77
|
1251 |
Warning: this may take some time."
|
1
|
1252 |
%{
|
|
1253 |
markAndSweep(__context);
|
|
1254 |
%}
|
|
1255 |
|
93
|
1256 |
"
|
133
|
1257 |
ObjectMemory markAndSweep
|
|
1258 |
"
|
1
|
1259 |
!
|
|
1260 |
|
|
1261 |
gcStep
|
77
|
1262 |
"one incremental garbage collect step.
|
|
1263 |
Mark or sweep some small number of objects. This
|
85
|
1264 |
method will return after a reasonable (short) time.
|
133
|
1265 |
This is used by the ProcessorScheduler at idle times.
|
|
1266 |
Returns true, if an incremental GC cycle has finished."
|
1
|
1267 |
%{
|
133
|
1268 |
extern int __incrGCstep();
|
|
1269 |
|
|
1270 |
RETURN (__incrGCstep(__context) ? true : false);
|
1
|
1271 |
%}
|
|
1272 |
!
|
|
1273 |
|
133
|
1274 |
incrementalGC
|
|
1275 |
"perform one round of incremental GC steps.
|
202
|
1276 |
The overall effect of this method is the same as calling markAndSweep.
|
133
|
1277 |
However, #incrementalGC is interruptable while #markAndSweep
|
|
1278 |
blocks for a while. Thus this method can be called from a low
|
|
1279 |
prio (background) process to collect without disturbing
|
159
|
1280 |
foreground processes too much.
|
|
1281 |
For example, someone allocating huge amounts of memory could
|
|
1282 |
ask for the possibility of a quick allocation using
|
|
1283 |
#checkForFastNew: and try a #incrementalGC if not. In many
|
270
|
1284 |
cases, this can avoid a pause (in the higher prio processes) due to
|
|
1285 |
a blocking GC."
|
133
|
1286 |
|
|
1287 |
[self gcStep] whileFalse:[]
|
|
1288 |
|
|
1289 |
"
|
|
1290 |
ObjectMemory incrementalGC
|
|
1291 |
"
|
194
|
1292 |
!
|
|
1293 |
|
270
|
1294 |
gcStepIfUseful
|
|
1295 |
"If either the IncrementalGCLimit or the FreeSpaceGCLimits have been
|
|
1296 |
reached, perform one incremental garbage collect step and return true.
|
|
1297 |
Otherwise do nothing and return false.
|
|
1298 |
This is called by the ProcessorScheduler at idle times."
|
|
1299 |
|
|
1300 |
|doingGC limit|
|
|
1301 |
|
|
1302 |
limit := IncrementalGCLimit.
|
|
1303 |
doingGC := limit notNil and:[self oldSpaceAllocatedSinceLastGC > limit].
|
|
1304 |
doingGC ifFalse:[
|
|
1305 |
limit := FreeSpaceGCLimit.
|
|
1306 |
doingGC := limit notNil and:[(self freeSpace + self freeListSpace) < limit].
|
|
1307 |
].
|
|
1308 |
doingGC ifFalse:[^ false].
|
|
1309 |
^ ObjectMemory gcStep not.
|
|
1310 |
!
|
|
1311 |
|
194
|
1312 |
verboseGarbageCollect
|
|
1313 |
"perform a compessing garbage collect and show some informational
|
|
1314 |
output on the Transcript"
|
|
1315 |
|
|
1316 |
|nBytesBefore nReclaimed|
|
|
1317 |
|
|
1318 |
nBytesBefore := self oldSpaceUsed.
|
|
1319 |
self garbageCollect.
|
|
1320 |
nReclaimed := nBytesBefore - self oldSpaceUsed.
|
|
1321 |
nReclaimed > 0 ifTrue:[
|
|
1322 |
Transcript show:'reclaimed '.
|
|
1323 |
nReclaimed > 1024 ifTrue:[
|
|
1324 |
nReclaimed > (1024 * 1024) ifTrue:[
|
|
1325 |
Transcript show:(nReclaimed // (1024 * 1024)) printString.
|
|
1326 |
Transcript showCr:' Mb.'
|
|
1327 |
] ifFalse:[
|
|
1328 |
Transcript show:(nReclaimed // 1024) printString.
|
|
1329 |
Transcript showCr:' Kb.'
|
|
1330 |
]
|
|
1331 |
] ifFalse:[
|
|
1332 |
Transcript show:nReclaimed printString.
|
|
1333 |
Transcript showCr:' bytes.'
|
|
1334 |
]
|
|
1335 |
]
|
|
1336 |
|
|
1337 |
"
|
|
1338 |
ObjectMemory verboseGarbageCollect
|
|
1339 |
"
|
207
|
1340 |
!
|
|
1341 |
|
|
1342 |
startBackgroundCollectorAt:aPriority
|
|
1343 |
"start a process doing incremental GC in the background.
|
|
1344 |
Use this, if you have suspendable background processes which
|
|
1345 |
run all the time, and therefore would prevent the idle-collector
|
|
1346 |
from running. See documentation in this class for more details."
|
|
1347 |
|
270
|
1348 |
"/
|
|
1349 |
"/ its not useful, to run it more than once
|
|
1350 |
"/
|
207
|
1351 |
BackgroundCollectProcess notNil ifTrue:[
|
|
1352 |
BackgroundCollectProcess priority:aPriority.
|
|
1353 |
^ self
|
|
1354 |
].
|
270
|
1355 |
|
207
|
1356 |
BackgroundCollectProcess :=
|
|
1357 |
[
|
211
|
1358 |
[true] whileTrue:[
|
270
|
1359 |
self gcStepIfUseful ifTrue:[
|
|
1360 |
"
|
|
1361 |
perform a full cycle
|
|
1362 |
"
|
|
1363 |
self incrementalGC
|
211
|
1364 |
].
|
|
1365 |
"
|
|
1366 |
wait a bit
|
|
1367 |
"
|
216
|
1368 |
(Delay forSeconds:5) wait
|
211
|
1369 |
]
|
207
|
1370 |
] newProcess.
|
|
1371 |
BackgroundCollectProcess name:'background collector'.
|
|
1372 |
BackgroundCollectProcess priority:aPriority.
|
|
1373 |
BackgroundCollectProcess resume
|
211
|
1374 |
|
|
1375 |
"
|
216
|
1376 |
ObjectMemory incrementalGCLimit:10000.
|
211
|
1377 |
ObjectMemory startBackgroundCollectorAt:5
|
|
1378 |
"
|
207
|
1379 |
!
|
|
1380 |
|
|
1381 |
stopBackgroundCollector
|
|
1382 |
"stop the background collector"
|
|
1383 |
|
|
1384 |
BackgroundCollectProcess notNil ifTrue:[
|
|
1385 |
BackgroundCollectProcess terminate.
|
|
1386 |
BackgroundCollectProcess := nil
|
|
1387 |
]
|
211
|
1388 |
"
|
|
1389 |
ObjectMemory stopBackgroundCollector
|
|
1390 |
"
|
178
|
1391 |
! !
|
|
1392 |
|
|
1393 |
!ObjectMemory class methodsFor:'garbage collector control'!
|
133
|
1394 |
|
270
|
1395 |
freeSpaceGCLimit:aNumber
|
|
1396 |
"set the freeSpace limit for incremental GC activation.
|
|
1397 |
The system will start doing incremental background GC, once less than this number
|
|
1398 |
of bytes are available in the compact free space.
|
|
1399 |
The default is nil; setting it to nil will turn this trigger off.
|
|
1400 |
This is EXPERIMENTAL; dont set it to non-nil, since it may lead to
|
|
1401 |
a looping collect if after the IGC, the freeSpace does not climb above
|
|
1402 |
the limit."
|
|
1403 |
|
|
1404 |
FreeSpaceGCLimit := aNumber
|
|
1405 |
|
|
1406 |
"
|
|
1407 |
ObjectMemory freeSpaceGCLimit:100000.
|
|
1408 |
ObjectMemory freeSpaceGCLimit:nil.
|
|
1409 |
"
|
|
1410 |
!
|
|
1411 |
|
|
1412 |
freeSpaceGCLimit
|
|
1413 |
"return the freeSpace limit for incremental GC activation.
|
|
1414 |
The system will start doing incremental background GC, once less than this number
|
|
1415 |
of bytes are available in the compact free space.
|
|
1416 |
The default is 100000; setting it to nil will turn this trigger off."
|
|
1417 |
|
|
1418 |
^ FreeSpaceGCLimit
|
|
1419 |
|
|
1420 |
"
|
|
1421 |
ObjectMemory freeSpaceGCLimit
|
|
1422 |
"
|
|
1423 |
!
|
|
1424 |
|
2
|
1425 |
incrementalGCLimit:aNumber
|
270
|
1426 |
"set the allocatedSinceLastGC limit for incremental GC activation.
|
|
1427 |
The system will start doing incremental background GC, once more than this number
|
|
1428 |
of bytes have been allocated since the last GC.
|
|
1429 |
The default is 500000; setting it to nil will turn this trigger off."
|
2
|
1430 |
|
|
1431 |
IncrementalGCLimit := aNumber
|
|
1432 |
|
85
|
1433 |
"
|
213
|
1434 |
ObjectMemory incrementalGCLimit:500000. 'do incr. GC very seldom'
|
|
1435 |
ObjectMemory incrementalGCLimit:100000. 'medium'
|
|
1436 |
ObjectMemory incrementalGCLimit:10000. 'do incr. GC very often'
|
|
1437 |
ObjectMemory incrementalGCLimit:nil. 'never'
|
85
|
1438 |
"
|
2
|
1439 |
!
|
|
1440 |
|
13
|
1441 |
incrementalGCLimit
|
270
|
1442 |
"return the allocatedSinceLastGC limit for incremental GC activation.
|
|
1443 |
The system will start doing incremental background GC, once more than this number
|
|
1444 |
of bytes have been allocated since the last GC.
|
|
1445 |
The default is 500000; setting it to nil will turn this trigger off."
|
13
|
1446 |
|
|
1447 |
^ IncrementalGCLimit
|
|
1448 |
|
85
|
1449 |
"
|
|
1450 |
ObjectMemory incrementalGCLimit
|
|
1451 |
"
|
13
|
1452 |
!
|
|
1453 |
|
133
|
1454 |
moreOldSpace:howMuch
|
|
1455 |
"allocate howMuch bytes more for old objects.
|
|
1456 |
This is done automatically, when running out of space, but makes
|
|
1457 |
sense, if its known in advance that a lot of memory is needed to
|
|
1458 |
avoid multiple reallocations and compresses.
|
|
1459 |
This (currently) implies a compressing garbage collect - so its slow.
|
|
1460 |
Notice: this is a nonstandard interface - use only in special situations."
|
|
1461 |
|
|
1462 |
%{
|
253
|
1463 |
if (__isSmallInteger(howMuch))
|
159
|
1464 |
__moreOldSpace(__context, _intVal(howMuch));
|
133
|
1465 |
%}
|
|
1466 |
"
|
|
1467 |
ObjectMemory moreOldSpace:1000000
|
|
1468 |
"
|
|
1469 |
!
|
|
1470 |
|
202
|
1471 |
announceSpaceNeed:howMuch
|
|
1472 |
"announce to the memory system, that howMuch bytes of memory will be needed
|
|
1473 |
soon. The VM tries to prepare itself for this allocation to be performed
|
|
1474 |
with less overhead. For example, it could preallocate some memory in one
|
|
1475 |
big chunk (instead of doing many smaller reallocations later).
|
|
1476 |
Notice: this is a nonstandard interface - use only in special situations.
|
|
1477 |
Also, this does a background collect before the big chunk of memory is
|
|
1478 |
allocated, not locking other processes while doing so."
|
|
1479 |
|
|
1480 |
(howMuch < (self newSpaceSize // 2)) ifTrue:[
|
|
1481 |
self scavenge.
|
|
1482 |
].
|
|
1483 |
(self checkForFastNew:howMuch) ifFalse:[
|
|
1484 |
(howMuch > (self newSpaceSize // 2)) ifFalse:[
|
|
1485 |
self scavenge.
|
|
1486 |
].
|
|
1487 |
self incrementalGC.
|
|
1488 |
(self checkForFastNew:howMuch) ifFalse:[
|
|
1489 |
self moreOldSpace:howMuch
|
|
1490 |
]
|
|
1491 |
]
|
|
1492 |
|
|
1493 |
"
|
|
1494 |
ObjectMemory announceSpaceNeed:100000
|
|
1495 |
"
|
|
1496 |
!
|
|
1497 |
|
133
|
1498 |
announceOldSpaceNeed:howMuch
|
202
|
1499 |
"announce to the memory system, that howMuch bytes of memory will be needed
|
|
1500 |
soon, which is going to live longer (whatever that means).
|
|
1501 |
It first checks if the memory can be allocated without forcing a compressing
|
|
1502 |
GC. If not, the oldSpace is increased. This may also lead to a slow compressing
|
133
|
1503 |
collect. However, many smaller increases are avoided afterwards. Calling this
|
|
1504 |
method before allocating huge chunks of data may provide better overall performance.
|
|
1505 |
Notice: this is a nonstandard interface - use only in special situations."
|
|
1506 |
|
|
1507 |
(self checkForFastNew:howMuch) ifFalse:[
|
159
|
1508 |
self incrementalGC.
|
|
1509 |
(self checkForFastNew:howMuch) ifFalse:[
|
|
1510 |
self moreOldSpace:howMuch
|
|
1511 |
]
|
133
|
1512 |
]
|
|
1513 |
|
|
1514 |
"
|
|
1515 |
ObjectMemory announceOldSpaceNeed:1000000
|
|
1516 |
"
|
|
1517 |
!
|
|
1518 |
|
|
1519 |
oldSpaceIncrement
|
|
1520 |
"return the oldSpaceIncrement value. Thats the amount by which
|
|
1521 |
more memory is allocated in case the oldSpace gets filled up.
|
|
1522 |
In normal situations, the default value used in the VM is fine
|
|
1523 |
and there is no need to change it."
|
178
|
1524 |
|
|
1525 |
%{ /* NOCONTEXT */
|
133
|
1526 |
extern unsigned __oldSpaceIncrement();
|
|
1527 |
|
|
1528 |
RETURN (_MKSMALLINT( __oldSpaceIncrement(-1) ));
|
|
1529 |
%}
|
178
|
1530 |
"
|
|
1531 |
ObjectMemory oldSpaceIncrement
|
|
1532 |
"
|
133
|
1533 |
!
|
|
1534 |
|
|
1535 |
oldSpaceIncrement:amount
|
|
1536 |
"set the oldSpaceIncrement value. Thats the amount by which
|
|
1537 |
more memory is allocated in case the oldSpace gets filled up.
|
|
1538 |
In normal situations, the default value used in the VM is fine
|
|
1539 |
and there is no need to change it. This method returns the
|
|
1540 |
previous increment value."
|
178
|
1541 |
|
|
1542 |
%{ /* NOCONTEXT */
|
133
|
1543 |
extern unsigned __oldSpaceIncrement();
|
|
1544 |
|
253
|
1545 |
if (__isSmallInteger(amount)) {
|
159
|
1546 |
RETURN (_MKSMALLINT( __oldSpaceIncrement(_intVal(amount)) ));
|
133
|
1547 |
}
|
|
1548 |
%}
|
178
|
1549 |
"to change increment to 1Meg:"
|
|
1550 |
"
|
|
1551 |
ObjectMemory oldSpaceIncrement:1024*1024
|
|
1552 |
"
|
|
1553 |
!
|
|
1554 |
|
|
1555 |
fastMoreOldSpaceAllocation:aBoolean
|
|
1556 |
"this method turns on/off fastMoreOldSpace allocation.
|
|
1557 |
By default, this is turned off (false), which means that in case of
|
202
|
1558 |
a filled-up oldSpace, a GC is tried first before more oldSpace is allocated.
|
178
|
1559 |
This strategy is ok for the normal operation of the system,
|
|
1560 |
but behaves badly, if the program allocates huge data structures (say a
|
202
|
1561 |
game tree of 30Mb in size) which survives and therefore will not be reclaimed
|
178
|
1562 |
by a GC.
|
202
|
1563 |
Of course while building this tree, and the memory becomes full, the system
|
|
1564 |
would not know in advance, that the GC will not reclaim anything.
|
|
1565 |
|
178
|
1566 |
Setting fastOldSpaceIncrement to true will avoid this, by forcing the
|
|
1567 |
memory system to allocate more memory right away, without doing a GC first.
|
|
1568 |
|
|
1569 |
WARNING: make certain that this flag is turned off, after your huge data
|
|
1570 |
is allocated, since otherwise the system may continue to increase its
|
202
|
1571 |
virtual memory without ever checking for garbage.
|
178
|
1572 |
This method returns the previous value of the flag."
|
|
1573 |
|
|
1574 |
%{ /* NOCONTEXT */
|
|
1575 |
RETURN (__fastMoreOldSpaceAllocation(aBoolean == true ? 1 : 0) ? true : false);
|
|
1576 |
%}
|
133
|
1577 |
!
|
|
1578 |
|
|
1579 |
checkForFastNew:amount
|
|
1580 |
"this method returns true, if amount bytes could be allocated
|
|
1581 |
quickly (i.e. without forcing a full GC or compress).
|
|
1582 |
This can be used for smart background processes, which want to
|
|
1583 |
allocate big chunks of data without disturbing foreground processes
|
|
1584 |
too much. Such a process would check for fast-allocation, and perform
|
202
|
1585 |
incremental GC-steps if required. Thus, avoiding the long blocking pause
|
|
1586 |
due to a forced (non-incremental) GC. Especially: doing so will not block
|
|
1587 |
higher priority foreground processes.
|
133
|
1588 |
This is experimental and not guaranteed to be in future versions."
|
|
1589 |
|
178
|
1590 |
%{ /* NOCONTEXT */
|
133
|
1591 |
extern __checkForFastNew();
|
|
1592 |
|
253
|
1593 |
if (__isSmallInteger(amount)) {
|
159
|
1594 |
if (! __checkForFastNew(_intVal(amount))) {
|
|
1595 |
RETURN (false);
|
|
1596 |
}
|
133
|
1597 |
}
|
|
1598 |
|
|
1599 |
%}.
|
|
1600 |
^ true
|
|
1601 |
!
|
|
1602 |
|
2
|
1603 |
turnGarbageCollectorOff
|
|
1604 |
"turn off garbage collector by forcing new objects to be
|
|
1605 |
allocated in oldSpace (instead of newSpace)
|
178
|
1606 |
WARNING:
|
93
|
1607 |
This is somewhat dangerous: if collector is turned off,
|
77
|
1608 |
and too many objects are created, the system may run into trouble
|
|
1609 |
(i.e. oldSpace becomes full) and be forced to perform a full mark&sweep
|
202
|
1610 |
or even a compressing collect - making the overall realtime behavior worse.
|
93
|
1611 |
Use this only for special purposes or when realtime behavior
|
178
|
1612 |
is required for a limited time period."
|
|
1613 |
|
|
1614 |
%{ /* NOCONTEXT */
|
133
|
1615 |
__allocForceSpace(OLDSPACE);
|
1
|
1616 |
%}
|
|
1617 |
!
|
|
1618 |
|
2
|
1619 |
turnGarbageCollectorOn
|
202
|
1620 |
"turn garbage collector on again (see ObjectMemory>>turnGarbageCollectorOff)"
|
1
|
1621 |
|
178
|
1622 |
%{ /* NOCONTEXT */
|
133
|
1623 |
__allocForceSpace(9999);
|
1
|
1624 |
%}
|
178
|
1625 |
!
|
85
|
1626 |
|
|
1627 |
makeOld:anObject
|
93
|
1628 |
"move anObject into oldSpace.
|
133
|
1629 |
This method is for internal & debugging purposes only -
|
|
1630 |
it may vanish. Dont use it."
|
85
|
1631 |
%{
|
|
1632 |
if (__moveToOldSpace(anObject, __context) < 0) {
|
159
|
1633 |
RETURN (false);
|
93
|
1634 |
}
|
|
1635 |
%}.
|
|
1636 |
^ true
|
194
|
1637 |
!
|
|
1638 |
|
|
1639 |
tenureParameters:magic
|
|
1640 |
"this is pure magic and not for public eyes ...
|
202
|
1641 |
This method allows fine tuning the scavenger internals,
|
194
|
1642 |
in cooperation to some statistic & test programs.
|
202
|
1643 |
It is undocumented, secret and may vanish.
|
|
1644 |
If you play around here, the system may behave very strange."
|
194
|
1645 |
|
|
1646 |
%{ /* NOCONTEXT */
|
|
1647 |
__tenureParams(magic);
|
|
1648 |
%}.
|
178
|
1649 |
! !
|
|
1650 |
|
|
1651 |
!ObjectMemory class methodsFor:'physical memory access'!
|
93
|
1652 |
|
|
1653 |
newSpacePagesDo:aBlock
|
|
1654 |
"evaluates aBlock for all pages in the newSpace, passing
|
|
1655 |
the pages address as argument.
|
|
1656 |
For internal & debugging use only."
|
|
1657 |
%{
|
|
1658 |
if (__newSpacePagesDo(&aBlock COMMA_CON) < 0) {
|
159
|
1659 |
RETURN (false);
|
85
|
1660 |
}
|
|
1661 |
%}.
|
|
1662 |
^ true
|
|
1663 |
!
|
|
1664 |
|
|
1665 |
oldSpacePagesDo:aBlock
|
|
1666 |
"evaluates aBlock for all pages in the oldSpace, passing
|
|
1667 |
the pages address as argument.
|
93
|
1668 |
For internal & debugging use only."
|
85
|
1669 |
%{
|
|
1670 |
if (__oldSpacePagesDo(&aBlock COMMA_CON) < 0) {
|
159
|
1671 |
RETURN (false);
|
85
|
1672 |
}
|
|
1673 |
%}.
|
|
1674 |
^ true
|
|
1675 |
!
|
|
1676 |
|
93
|
1677 |
collectedOldSpacePagesDo:aBlock
|
|
1678 |
"evaluates aBlock for all pages in the prev. oldSpace, passing
|
|
1679 |
the pages address as argument.
|
|
1680 |
For internal & debugging use only."
|
|
1681 |
%{
|
|
1682 |
if (__collectedOldSpacePagesDo(&aBlock COMMA_CON) < 0) {
|
159
|
1683 |
RETURN (false);
|
93
|
1684 |
}
|
|
1685 |
%}.
|
|
1686 |
^ true
|
|
1687 |
!
|
|
1688 |
|
85
|
1689 |
pageIsInCore:aPageNumber
|
|
1690 |
"return true, if the page (as enumerated via oldSpacePagesDo:)
|
|
1691 |
is in memory; false, if currently paged out. For internal
|
93
|
1692 |
use / monitors only; may vanish.
|
|
1693 |
NOTICE: not all systems provide this information; on those that
|
|
1694 |
do not, true is returned for all pages."
|
85
|
1695 |
%{
|
|
1696 |
#ifdef HAS_MINCORE
|
|
1697 |
int pageSize = getpagesize();
|
|
1698 |
char result[10];
|
|
1699 |
INT addr;
|
|
1700 |
|
253
|
1701 |
if (__isSmallInteger(aPageNumber)) {
|
159
|
1702 |
addr = _intVal(aPageNumber) & ~(pageSize - 1);
|
85
|
1703 |
} else {
|
159
|
1704 |
addr = ((INT)aPageNumber) & ~(pageSize - 1);
|
85
|
1705 |
}
|
|
1706 |
if (mincore(addr, pageSize-1, result) < 0) {
|
159
|
1707 |
RETURN (true);
|
85
|
1708 |
}
|
|
1709 |
RETURN ((result[0] & 1) ? true : false);
|
|
1710 |
#endif
|
|
1711 |
%}.
|
|
1712 |
"OS does not supply this info - assume yes"
|
|
1713 |
^ true
|
|
1714 |
! !
|
|
1715 |
|
13
|
1716 |
!ObjectMemory class methodsFor:'low memory handling'!
|
|
1717 |
|
|
1718 |
memoryInterrupt
|
|
1719 |
"when a low-memory condition arises, ask all classes to
|
|
1720 |
remove possibly cached data - this may help a bit"
|
|
1721 |
|
202
|
1722 |
Smalltalk allBehaviors do:[:aClass |
|
159
|
1723 |
aClass lowSpaceCleanup
|
13
|
1724 |
].
|
|
1725 |
|
178
|
1726 |
"/ self error:'almost out of memory'
|
|
1727 |
'almost out of memory' errorPrintNL.
|
|
1728 |
|
|
1729 |
LowSpaceSemaphore signalIf.
|
13
|
1730 |
! !
|
|
1731 |
|
1
|
1732 |
!ObjectMemory class methodsFor:'system management'!
|
|
1733 |
|
|
1734 |
loadClassBinary:aClassName
|
|
1735 |
"find the object file for aClassName and -if found - load it;
|
|
1736 |
this one loads precompiled object files"
|
|
1737 |
|
|
1738 |
|fName newClass upd|
|
|
1739 |
|
|
1740 |
fName := self fileNameForClass:aClassName.
|
|
1741 |
fName notNil ifTrue:[
|
202
|
1742 |
Class withoutUpdatingChangesDo:
|
159
|
1743 |
[
|
|
1744 |
self loadBinary:(fName , '.o')
|
|
1745 |
].
|
|
1746 |
newClass := self at:(aClassName asSymbol).
|
|
1747 |
(newClass notNil and:[newClass implements:#initialize]) ifTrue:[
|
|
1748 |
newClass initialize
|
|
1749 |
]
|
1
|
1750 |
]
|
|
1751 |
!
|
|
1752 |
|
77
|
1753 |
imageName
|
|
1754 |
"return the filename of the current image, or nil
|
|
1755 |
if not running from an image."
|
|
1756 |
|
|
1757 |
^ ImageName
|
159
|
1758 |
|
|
1759 |
"
|
202
|
1760 |
ObjectMemory imageName
|
|
1761 |
"
|
|
1762 |
!
|
|
1763 |
|
|
1764 |
imageBaseName
|
|
1765 |
"return a reasonable filename to use as baseName (i.e. without extension).
|
|
1766 |
This is the filename of the current image (without '.img') or,
|
|
1767 |
if not running from an image, the default name 'st'"
|
|
1768 |
|
|
1769 |
|nm|
|
|
1770 |
|
|
1771 |
nm := ImageName.
|
|
1772 |
(nm isNil or:[nm isBlank]) ifTrue:[
|
|
1773 |
^ 'st'
|
|
1774 |
].
|
|
1775 |
(nm endsWith:'.sav') ifTrue:[
|
|
1776 |
nm := nm copyTo:(nm size - 4)
|
|
1777 |
].
|
|
1778 |
(nm endsWith:'.img') ifTrue:[
|
|
1779 |
^ nm copyTo:(nm size - 4)
|
|
1780 |
].
|
|
1781 |
^ nm
|
|
1782 |
|
|
1783 |
"
|
|
1784 |
ObjectMemory imageBaseName
|
159
|
1785 |
"
|
77
|
1786 |
!
|
|
1787 |
|
22
|
1788 |
nameForSnapshot
|
159
|
1789 |
"return a reasonable filename to store the snapshot image into.
|
|
1790 |
This is the filename of the current image or,
|
22
|
1791 |
if not running from an image, the default name 'st.img'"
|
|
1792 |
|
202
|
1793 |
^ self imageBaseName , '.img'
|
|
1794 |
|
|
1795 |
"
|
|
1796 |
ObjectMemory nameForSnapshot
|
|
1797 |
"
|
|
1798 |
!
|
|
1799 |
|
|
1800 |
nameForSources
|
|
1801 |
"return a reasonable filename to store the sources into.
|
|
1802 |
This is the basename of the current image with '.img' replaced
|
|
1803 |
by '.src', or, if not running from an image, the default name 'st.src'"
|
|
1804 |
|
|
1805 |
^ self imageBaseName , '.src'
|
159
|
1806 |
|
|
1807 |
"
|
202
|
1808 |
ObjectMemory nameForSources
|
|
1809 |
"
|
|
1810 |
!
|
|
1811 |
|
|
1812 |
nameForChanges
|
|
1813 |
"return a reasonable filename to store the changes into.
|
|
1814 |
This is the basename of the current image with '.img' replaced
|
|
1815 |
by '.chg', or, if not running from an image, the default name 'st.chg'"
|
|
1816 |
|
|
1817 |
^ 'changes'.
|
|
1818 |
^ self imageBaseName , '.chg'
|
|
1819 |
|
|
1820 |
"
|
|
1821 |
ObjectMemory nameForChanges
|
159
|
1822 |
"
|
22
|
1823 |
!
|
|
1824 |
|
1
|
1825 |
snapShot
|
159
|
1826 |
"create a snapshot file containing all of the current state."
|
1
|
1827 |
|
22
|
1828 |
self snapShotOn:(self nameForSnapshot)
|
1
|
1829 |
|
159
|
1830 |
"
|
|
1831 |
ObjectMemory snapShot
|
|
1832 |
"
|
1
|
1833 |
!
|
|
1834 |
|
|
1835 |
snapShotOn:aFileName
|
|
1836 |
"create a snapshot in the given file"
|
|
1837 |
|
159
|
1838 |
|ok oldImageName|
|
|
1839 |
|
|
1840 |
"
|
|
1841 |
keep a save version - just in case something
|
|
1842 |
bad happens while writing the image.
|
|
1843 |
(could be st/x internal error or file-system errors etc)
|
|
1844 |
"
|
|
1845 |
(OperatingSystem isValidPath:aFileName) ifTrue:[
|
|
1846 |
OperatingSystem renameFile:aFileName to:(aFileName , '.sav').
|
|
1847 |
].
|
22
|
1848 |
|
93
|
1849 |
"
|
|
1850 |
give others a chance to fix things
|
|
1851 |
"
|
1
|
1852 |
self changed:#save.
|
13
|
1853 |
|
93
|
1854 |
"
|
|
1855 |
ST-80 compatibility; send #preSnapshot to all classes
|
|
1856 |
"
|
202
|
1857 |
Smalltalk allBehaviorsDo:[:aClass |
|
159
|
1858 |
aClass preSnapshot
|
93
|
1859 |
].
|
|
1860 |
|
159
|
1861 |
"
|
|
1862 |
save the name with it ...
|
|
1863 |
"
|
|
1864 |
oldImageName := ImageName.
|
|
1865 |
ImageName := aFileName.
|
|
1866 |
ok := self primSnapShotOn:aFileName.
|
|
1867 |
ImageName := oldImageName.
|
|
1868 |
|
|
1869 |
ok ifTrue:[
|
|
1870 |
Class addChangeRecordForSnapshot:aFileName.
|
|
1871 |
|
|
1872 |
|
|
1873 |
"
|
|
1874 |
ST-80 compatibility; send #postSnapshot to all classes
|
|
1875 |
"
|
202
|
1876 |
Smalltalk allBehaviorsDo:[:aClass |
|
159
|
1877 |
aClass postSnapshot
|
|
1878 |
].
|
|
1879 |
].
|
|
1880 |
^ ok
|
|
1881 |
|
|
1882 |
"
|
|
1883 |
ObjectMemory snapShotOn:'myimage.img'
|
|
1884 |
"
|
|
1885 |
!
|
|
1886 |
|
|
1887 |
primSnapShotOn:aFileName
|
|
1888 |
"create a snapshot in the given file.
|
|
1889 |
Low level entry. Does not notify classes or write an entry to
|
|
1890 |
the changes file. Also, no image backup is created. Returns true if
|
|
1891 |
the snapshot worked, false if it failed for some reason.
|
|
1892 |
This method should not be used in normal cases."
|
|
1893 |
|
|
1894 |
|ok|
|
|
1895 |
|
13
|
1896 |
%{ /* STACK:32000 */
|
|
1897 |
|
1
|
1898 |
OBJ __snapShotOn();
|
159
|
1899 |
OBJ funny = @symbol(funnySnapshotSymbol);
|
1
|
1900 |
|
56
|
1901 |
if (__isString(aFileName)) {
|
159
|
1902 |
BLOCKINTERRUPTS();
|
|
1903 |
ok = __snapShotOn(__context, _stringVal(aFileName), funny);
|
|
1904 |
UNBLOCKINTERRUPTS();
|
1
|
1905 |
}
|
159
|
1906 |
%}.
|
22
|
1907 |
^ ok
|
1
|
1908 |
!
|
|
1909 |
|
|
1910 |
applicationImageOn:aFileName for:startupClass selector:startupSelector
|
|
1911 |
"create a snapshot which will come up without any views
|
93
|
1912 |
but starts up an application by sending startupClass the startupSelector.
|
|
1913 |
EXPERIMENTAL and unfinished. Dont use this method."
|
1
|
1914 |
|
2
|
1915 |
|viewsKnown savedIdleBlocks savedTimeoutBlocks savedTranscript
|
|
1916 |
savedRoot|
|
1
|
1917 |
|
|
1918 |
viewsKnown := Display knownViews.
|
|
1919 |
savedIdleBlocks := Display idleBlocks.
|
|
1920 |
savedTimeoutBlocks := Display timeOutBlocks.
|
|
1921 |
savedTranscript := Transcript.
|
2
|
1922 |
savedRoot := RootView.
|
1
|
1923 |
|
10
|
1924 |
"a kludge: save image with modified knownViews, no idle- and timeoutblocks
|
|
1925 |
and also Transcript set to StdErr ..."
|
1
|
1926 |
|
|
1927 |
Display knownViews:nil.
|
|
1928 |
Display idleBlocks:nil.
|
|
1929 |
Display timeOutBlocks:nil.
|
2
|
1930 |
RootView := nil.
|
|
1931 |
|
1
|
1932 |
Transcript := Stderr.
|
10
|
1933 |
Smalltalk startupClass:startupClass selector:startupSelector arguments:nil.
|
1
|
1934 |
self snapShotOn:aFileName.
|
10
|
1935 |
Smalltalk startupClass:nil selector:nil arguments:nil.
|
1
|
1936 |
|
2
|
1937 |
RootView := savedRoot.
|
1
|
1938 |
Transcript := savedTranscript.
|
|
1939 |
Display knownViews:viewsKnown.
|
|
1940 |
Display idleBlocks:savedIdleBlocks.
|
|
1941 |
Display timeOutBlocks:savedTimeoutBlocks
|
|
1942 |
|
|
1943 |
"ObjectMemory applicationImageOn:'draw.img' for:DrawTool selector:#start"
|
|
1944 |
"ObjectMemory applicationImageOn:'pm.img' for:PMSimulator selector:#start"
|
|
1945 |
!
|
|
1946 |
|
|
1947 |
minimumApplicationImageOn:aFileName for:startupClass selector:startupSelector
|
|
1948 |
"create a snapshot which will come up without any views
|
|
1949 |
but starts up an application by sending startupClass the startupSelector.
|
93
|
1950 |
All unneeded info is stripped from the saved image.
|
|
1951 |
EXPERIMENTAL and unfinished. Dont use this method."
|
1
|
1952 |
|
|
1953 |
"create a temporary image, for continuation"
|
|
1954 |
self snapShotOn:'temp.img'.
|
|
1955 |
|
|
1956 |
Display knownViews do:[:aView |
|
159
|
1957 |
aView notNil ifTrue:[
|
|
1958 |
aView superView isNil ifTrue:[
|
|
1959 |
aView destroy
|
|
1960 |
]
|
|
1961 |
]
|
1
|
1962 |
].
|
|
1963 |
|
|
1964 |
self stripImage.
|
|
1965 |
|
|
1966 |
self applicationImageOn:aFileName for:startupClass selector:startupSelector.
|
|
1967 |
|
|
1968 |
"continue in old image"
|
|
1969 |
|
|
1970 |
OperatingSystem exec:(Arguments at:1)
|
159
|
1971 |
withArguments:#('smalltalk' '-i' 'temp.img') , (Arguments copyFrom:2)
|
1
|
1972 |
|
|
1973 |
"ObjectMemory minimumApplicationImageOn:'clock1.img' for:Clock selector:#start"
|
|
1974 |
"ObjectMemory applicationImageOn:'clock2.img' for:Clock selector:#start"
|
|
1975 |
!
|
|
1976 |
|
|
1977 |
stripImage
|
93
|
1978 |
"remove all unneeded stuff from the image - much more is possible here.
|
|
1979 |
EXPERIMENTAL and unfinished. Dont use this method."
|
1
|
1980 |
|
2
|
1981 |
"remove all class comments & source"
|
1
|
1982 |
|
2
|
1983 |
Smalltalk allBehaviorsDo:[:aClass |
|
159
|
1984 |
aClass setComment:nil.
|
|
1985 |
aClass methodArray do:[:aMethod |
|
|
1986 |
aMethod source:''.
|
|
1987 |
aMethod category:#none
|
|
1988 |
]
|
1
|
1989 |
].
|
|
1990 |
self garbageCollect
|
|
1991 |
! !
|