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