Hi Sven,
On Wed, Oct 23, 2013 at 5:52 AM, Sven Van Caekenberghe <sven(a)stfx.eu> wrote:
>
> On 23 Oct 2013, at 14:22, Igor Stasenko <siguctua(a)gmail.com> wrote:
>
> > Yeah, i was also exploring, why even if i do not touch image i got like
> 3-5% CPU load.
> > What i found that it is in VM. Because i basicaly removed everything in
> image,
> > and just left idle process, and still had such loads.
>
> Igor, some time ago you told me which thread inside the VM was doing this,
> but maybe you forgot ;-)
>
two different things. One is the idle process, in the image. This is
heavy-weight. It calls relinquishProcessorForMicroseconds: which does all
sorts of things, including system calls to poll for i/o. The other is the
poll thread in the Cog and Stack VMs. This simply sleeps for a
millisecond, wakes up and sets a variable, and then goes back to sleep. It
is relatively lightweight.
I suspect the 3-5% is due to the former, not the latter. One way to test
would be to compare the interpreter VM against the Stack and Cog VMs and
see how much time the interpreter VM takes. If the interpreter VM also
takes a lot of time at idle, then the poll thread is not the culprit.
> > On 23 October 2013 13:52, Sven Van Caekenberghe <sven(a)stfx.eu> wrote:
> > Hi,
> >
> > This morning I upgraded my MacBook Air to Mac OS X 10.9 Mavericks. The
> good new is that my trusty VM [1] is still working OK !
> >
> > However, something seems to have changed regarding full screen mode: it
> more or less works but there are two black bands and the window title
> remains visible, it is as if it did not expand enough:
> >
> > <Screen Shot 2013-10-23 at 13.40.56.png>
> >
> > Which is a pity: I rely on this a lot to get more screen space on my 13
> inch screen and to work distraction free.
> >
> > I guess we’ll have to wait for our hero Esteban to have a look.
> >
> > Also new is the focus on energy management and efficiency. Sadly Pharo
> is flagged as using to much (look at the menu under the battery icon top
> right and the energy tab in the activity monitor):
> >
> > <Screen Shot 2013-10-23 at 13.49.53.png>
> >
> > This is probably related to the busy thread running/polling every ms or
> so inside the VM.
> >
> > I could turn these into issues.
> >
> > Sven
> >
> > [1]
> >
> > Virtual Machine
> > ---------------
> > /Users/sven/Develop/Smalltalk/Pharo.app/Contents/MacOS/Pharo
> > NBCoInterpreter NativeBoost-CogPlugin-EstebanLorenzano.18 uuid:
> a53445f9-c0c0-4015-97a3-be7db8d9ed6b Mar 13 2013
> > NBCogit NativeBoost-CogPlugin-EstebanLorenzano.18 uuid:
> a53445f9-c0c0-4015-97a3-be7db8d9ed6b Mar 13 2013
> > git://gitorious.org/cogvm/blessed.git Commit:
> 412abef33cbed05cf1d75329e451d71c0c6aa5a7 Date: 2013-03-13 17:48:50 +0100
> By: Esteban Lorenzano <estebanlm(a)gmail.com> Jenkins build #14535
> >
> > Mac Cocoa Cog 5.8b12 21-Sep-10 >1B0534FA-246C-47C5-AB29-7A76C81CCDCB<
> > VMMaker versionString git://gitorious.org/cogvm/blessed.git Commit:
> 412abef33cbed05cf1d75329e451d71c0c6aa5a7 Date: 2013-03-13 17:48:50 +0100
> By: Esteban Lorenzano <estebanlm(a)gmail.com> Jenkins build #14535
> > NBCoInterpreter NativeBoost-CogPlugin-EstebanLorenzano.18 uuid:
> a53445f9-c0c0-4015-97a3-be7db8d9ed6b Mar 13 2013
> > NBCogit NativeBoost-CogPlugin-EstebanLorenzano.18 uuid:
> a53445f9-c0c0-4015-97a3-be7db8d9ed6b Mar 13 2013
> >
> >
> >
> >
> >
> > --
> > Best regards,
> > Igor Stasenko.
>
>
>
--
best,
Eliot
Hello,
we've been experiencing memory leakage in long running Squeak images using
SqueakSSL. After a bit of monitoring I found that 132 bytes get leaked
for each https request done from the image. After a bit of code review,
I've probably found the culprit, and another potential source of memory
leak. For the reference, the source file this mail is about is
http://squeakvm.org/cgi-bin/viewvc.cgi/squeak/branches/Cog/platforms/unix/p…
commits(a)source.squeak.org wrote:
>
> Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
> http://source.squeak.org/VMMaker/VMMaker.oscog-eem.399.mcz
>
> ==================== Summary ====================
>
> Name: VMMaker.oscog-eem.399
> Author: eem
> Time: 20 September 2013, 6:28:56.308 pm
> UUID: 89f8fefe-b59d-42d7-9c11-7f848d0e5131
> Ancestors: VMMaker.oscog-eem.398
>
> A few isIntegerObject:'s => isImmediate:'s in primitives.
>
> More protocol.
>
> The Spur VM now draws its first window!!
>
>
A cheer goes up in the crowd of interested spectators.
Probably lots still to do, but its a nice concrete milestone.
Contributing is beyond me at this time, so I especially like to thank
you for this important initiative.
cheers -ben
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.472.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.472
Author: eem
Time: 21 October 2013, 1:19:49.652 pm
UUID: 88a5a8b4-a29f-43fd-9a4e-ff5319cf5c4d
Ancestors: VMMaker.oscog-eem.471
Make rebuildFreeTreeFromSortedFreeChunks sort the lists off each
tree node in the free tree for compaction, highest address for the
node, followed by a list from low to high.
Requires freeTreeNodesDo: to update its notion of the node from
its block. This in turn requires Slang to handle outputting
an assignment whose value is a value expansion correctly, moving
the assignment to the last statement of the value expansion.
speeling errars, coallesce => coalesce, detatch => detach.
Now ready to implement bestBitCompact & exactFitCompact properly.
=============== Diff against VMMaker.oscog-eem.471 ===============
Item was added:
+ ----- Method: Spur32BitMemoryManager>>coalesce:and: (in category 'gc - global') -----
+ coalesce: obj1 and: obj2
+ | header1NumSlots header2NumSlots obj2slots newNumSlots |
+ header1NumSlots := self rawNumSlotsOf: obj1.
+ header2NumSlots := self rawNumSlotsOf: obj2.
+
+ "compute total number of slots in obj2, including header"
+ obj2slots := header2NumSlots = self numSlotsMask
+ ifTrue: [(self longAt: obj2 - self baseHeaderSize) + (2 * self baseHeaderSize / self wordSize)]
+ ifFalse: [(header2NumSlots = 0 ifTrue: [1] ifFalse: [header2NumSlots]) + (self baseHeaderSize / self wordSize)].
+ obj2slots := obj2slots + (obj2slots bitAnd: 1).
+ self assert: obj2slots * self wordSize = (self bytesInObject: obj2).
+
+ "if obj1 already has a double header things are simple..."
+ header1NumSlots = self numSlotsMask ifTrue:
+ [self longAt: obj1 - self baseHeaderSize put: obj2slots + (self longAt: obj1 - self baseHeaderSize).
+ ^obj1].
+
+ "compute total number of slots in obj1, excluding header"
+ header1NumSlots := header1NumSlots = 0
+ ifTrue: [2]
+ ifFalse: [header1NumSlots + (header1NumSlots bitAnd: 1)].
+ self assert: header1NumSlots * self wordSize + self baseHeaderSize = (self bytesInObject: obj1).
+ newNumSlots := obj2slots + header1NumSlots.
+
+ "if obj1 still only requires a single header things are simple..."
+ newNumSlots < self numSlotsMask ifTrue:
+ [self byteAt: obj1 + (self numSlotsFullShift / BitsPerByte)
+ put: newNumSlots.
+ ^obj1].
+
+ "convert from single to double header..."
+ newNumSlots := newNumSlots - (self baseHeaderSize / self wordSize).
+ self longAt: obj1 + self baseHeaderSize
+ put: (self longAt: obj1);
+ longAt: obj1 + 4 + self baseHeaderSize
+ put: ((self longAt: obj1 + 4) bitOr: self numSlotsMask << self numSlotsHalfShift).
+ self longAt: obj1
+ put: newNumSlots.
+ self longAt: obj1 + 4
+ put: self numSlotsMask << self numSlotsHalfShift.
+ ^obj1 + self baseHeaderSize!
Item was removed:
- ----- Method: Spur32BitMemoryManager>>coallesce:and: (in category 'gc - global') -----
- coallesce: obj1 and: obj2
- | header1NumSlots header2NumSlots obj2slots newNumSlots |
- header1NumSlots := self rawNumSlotsOf: obj1.
- header2NumSlots := self rawNumSlotsOf: obj2.
-
- "compute total number of slots in obj2, including header"
- obj2slots := header2NumSlots = self numSlotsMask
- ifTrue: [(self longAt: obj2 - self baseHeaderSize) + (2 * self baseHeaderSize / self wordSize)]
- ifFalse: [(header2NumSlots = 0 ifTrue: [1] ifFalse: [header2NumSlots]) + (self baseHeaderSize / self wordSize)].
- obj2slots := obj2slots + (obj2slots bitAnd: 1).
- self assert: obj2slots * self wordSize = (self bytesInObject: obj2).
-
- "if obj1 already has a double header things are simple..."
- header1NumSlots = self numSlotsMask ifTrue:
- [self longAt: obj1 - self baseHeaderSize put: obj2slots + (self longAt: obj1 - self baseHeaderSize).
- ^obj1].
-
- "compute total number of slots in obj1, excluding header"
- header1NumSlots := header1NumSlots = 0
- ifTrue: [2]
- ifFalse: [header1NumSlots + (header1NumSlots bitAnd: 1)].
- self assert: header1NumSlots * self wordSize + self baseHeaderSize = (self bytesInObject: obj1).
- newNumSlots := obj2slots + header1NumSlots.
-
- "if obj1 still only requires a single header things are simple..."
- newNumSlots < self numSlotsMask ifTrue:
- [self byteAt: obj1 + (self numSlotsFullShift / BitsPerByte)
- put: newNumSlots.
- ^obj1].
-
- "convert from single to double header..."
- newNumSlots := newNumSlots - (self baseHeaderSize / self wordSize).
- self longAt: obj1 + self baseHeaderSize
- put: (self longAt: obj1);
- longAt: obj1 + 4 + self baseHeaderSize
- put: ((self longAt: obj1 + 4) bitOr: self numSlotsMask << self numSlotsHalfShift).
- self longAt: obj1
- put: newNumSlots.
- self longAt: obj1 + 4
- put: self numSlotsMask << self numSlotsHalfShift.
- ^obj1 + self baseHeaderSize!
Item was changed:
CogClass subclass: #SpurMemoryManager
(excessive size, no diff calculated)
Item was changed:
----- Method: SpurMemoryManager>>adjustAllOopsBy: (in category 'snapshot') -----
adjustAllOopsBy: bytesToShift
"Adjust all oop references by the given number of bytes. This is
done just after reading in an image when the new base address
of the object heap is different from the base address in the image,
+ or when loading multiple segments that have been coalesced. Also
- or when loading multiple segments that have been coallesced. Also
set bits in the classTableBitmap corresponding to used classes."
| obj |
self countNumClassPagesPreSwizzle: bytesToShift;
ensureAdequateClassTableBitmap.
(bytesToShift ~= 0
or: [segmentManager numSegments > 1])
ifTrue:
[self assert: self newSpaceIsEmpty.
obj := self objectStartingAt: newSpaceLimit.
[self oop: obj isLessThan: freeOldSpaceStart] whileTrue:
[(self isFreeObject: obj)
ifTrue: [self swizzleFieldsOfFreeChunk: obj]
ifFalse:
[self inClassTableBitmapSet: (self classIndexOf: obj).
self swizzleFieldsOfObject: obj].
obj := self objectAfter: obj]]
ifFalse:
[self assert: self newSpaceIsEmpty.
obj := self objectStartingAt: newSpaceLimit.
[self oop: obj isLessThan: freeOldSpaceStart] whileTrue:
[(self isFreeObject: obj) ifFalse:
[self inClassTableBitmapSet: (self classIndexOf: obj)].
obj := self objectAfter: obj]]!
Item was changed:
----- Method: SpurMemoryManager>>allObjectsInFreeTreeDo: (in category 'free space') -----
allObjectsInFreeTreeDo: aBlock
"Enumerate all objects in the free tree (in order, smaller to larger).
This is an iterative version so that the block argument can be
inlined by Slang. The trick to an iterative binary tree application is
to apply the function on the way back up when returning from a
particular direction, in this case up from the larger child."
<inline: true>
self freeTreeNodesDo:
[:freeTreeNode| | next |
next := freeTreeNode.
[aBlock value: next.
next := self fetchPointer: self freeChunkNextIndex ofFreeChunk: next.
+ next ~= 0] whileTrue.
+ freeTreeNode]!
- next ~= 0] whileTrue]!
Item was added:
+ ----- Method: SpurMemoryManager>>allOldSpaceEntitiesForCoalescingDo: (in category 'object enumeration') -----
+ allOldSpaceEntitiesForCoalescingDo: aBlock
+ <inline: true>
+ | prevObj prevPrevObj objOop rawNumSlots rawNumSlotsAfter |
+ prevPrevObj := prevObj := nil.
+ objOop := self firstObject.
+ [self assert: objOop \\ self allocationUnit = 0.
+ objOop < freeOldSpaceStart] whileTrue:
+ [rawNumSlots := self rawNumSlotsOf: objOop.
+ aBlock value: objOop.
+ "If the number of slot changes coalescing changed an object from a single to a double header."
+ rawNumSlotsAfter := self rawNumSlotsOf: objOop.
+ (rawNumSlotsAfter ~= rawNumSlots
+ and: [rawNumSlotsAfter = self numSlotsMask]) ifTrue:
+ [objOop := objOop + self baseHeaderSize.
+ self assert: (self objectAfter: prevObj limit: freeOldSpaceStart) = objOop].
+ prevPrevObj := prevObj.
+ prevObj := objOop.
+ objOop := self objectAfter: objOop limit: freeOldSpaceStart].
+ self touch: prevPrevObj.
+ self touch: prevObj!
Item was removed:
- ----- Method: SpurMemoryManager>>allOldSpaceEntitiesForCoallescingDo: (in category 'object enumeration') -----
- allOldSpaceEntitiesForCoallescingDo: aBlock
- <inline: true>
- | prevObj prevPrevObj objOop rawNumSlots rawNumSlotsAfter |
- prevPrevObj := prevObj := nil.
- objOop := self firstObject.
- [self assert: objOop \\ self allocationUnit = 0.
- objOop < freeOldSpaceStart] whileTrue:
- [rawNumSlots := self rawNumSlotsOf: objOop.
- aBlock value: objOop.
- "If the number of slot changes coallescing changed an object from a single to a double header."
- rawNumSlotsAfter := self rawNumSlotsOf: objOop.
- (rawNumSlotsAfter ~= rawNumSlots
- and: [rawNumSlotsAfter = self numSlotsMask]) ifTrue:
- [objOop := objOop + self baseHeaderSize.
- self assert: (self objectAfter: prevObj limit: freeOldSpaceStart) = objOop].
- prevPrevObj := prevObj.
- prevObj := objOop.
- objOop := self objectAfter: objOop limit: freeOldSpaceStart].
- self touch: prevPrevObj.
- self touch: prevObj!
Item was changed:
----- Method: SpurMemoryManager>>bestFitCompact (in category 'compaction') -----
bestFitCompact
+ "Compact all of memory using best-fit, assuming free space is sorted
+ and that the highest objects are recorded in highestObjects."
- "Compact all of memory using best-fit.
- Sort free space, find the first free chunk. Scan objects following
- the first free chunk and for each, look for a free chunk of the exact
- size, copy the object's contents into the free chunk, and forward
- the object to its new, lower location. If any didn't fit exactly do a
- second pass using best fit."
| firstFailedFit |
firstFailedFit := self exactFitCompact.
firstFailedFit = 0 ifTrue:
[^self]. "either no free space, no high objects, or no misfits."
self allOldSpaceObjectsFrom: firstFailedFit
do: [:o| | b |
((self isForwarded: o)
or: [self isPinned: o]) ifFalse:
[b := self bytesInObject: o.
+ (self allocateOldSpaceChunkOfBytes: b suchThat: [:f| f < o]) ifNotNil:
- (self allocateOldSpaceChunkOfBytes: b) ifNotNil:
[:f|
self mem: f
cp: o
y: ((self hasOverflowHeader: o)
ifTrue: [b - self baseHeaderSize]
ifFalse: [b]).
(self isRemembered: o) ifTrue:
[scavenger remember: f].
self forward: o to: f].
self checkFreeSpace]].
self checkFreeSpace!
Item was removed:
- ----- Method: SpurMemoryManager>>buildPrevLinksForLargeFreeChunks (in category 'free space') -----
- buildPrevLinksForLargeFreeChunks
- | prevTreeNode |
- prevTreeNode := 0.
- self freeTreeNodesDo:
- [:freeTreeNode| | next prev |
- "freeTreeNodesDo: is supposed to be an in-order traversal from smaller to larger..."
- self assert: (prevTreeNode = 0 or: [(self numSlotsOfAny: prevTreeNode) < (self numSlotsOfAny: freeTreeNode)]).
- self storePointer: self freeChunkPrevIndex ofFreeChunk: freeTreeNode withValue: 0.
- prev := freeTreeNode.
- [(next := self fetchPointer: self freeChunkNextIndex ofObject: prev) ~= 0] whileTrue:
- [self storePointer: self freeChunkPrevIndex ofFreeChunk: next withValue: prev.
- prev := next]]!
Item was added:
+ ----- Method: SpurMemoryManager>>coalesce:and: (in category 'gc - global') -----
+ coalesce: obj1 and: obj2
+ self subclassResponsibility!
Item was removed:
- ----- Method: SpurMemoryManager>>coallesce:and: (in category 'gc - global') -----
- coallesce: obj1 and: obj2
- self subclassResponsibility!
Item was added:
+ ----- Method: SpurMemoryManager>>detachLargeFreeObject: (in category 'free space') -----
+ detachLargeFreeObject: freeChunk
+ | prev next |
+ prev := self fetchPointer: self freeChunkPrevIndex ofObject: freeChunk.
+ next := self fetchPointer: self freeChunkNextIndex ofObject: freeChunk.
+ prev = 0
+ ifTrue: "freeChunk is a treeNode"
+ [next = 0
+ ifTrue: "remove it from the tree"
+ [self unlinkSolitaryFreeTreeNode: freeChunk]
+ ifFalse: "replace freeChunk by its next node."
+ [self unlinkFreeTreeNode: freeChunk withSiblings: next]]
+ ifFalse: "freeChunk is a list node; simple"
+ [self storePointer: self freeChunkNextIndex ofFreeChunk: prev withValue: next.
+ next ~= 0 ifTrue:
+ [self storePointer: self freeChunkPrevIndex ofFreeChunk: next withValue: prev]]!
Item was removed:
- ----- Method: SpurMemoryManager>>detatchLargeFreeObject: (in category 'free space') -----
- detatchLargeFreeObject: freeChunk
- | prev next |
- prev := self fetchPointer: self freeChunkPrevIndex ofObject: freeChunk.
- next := self fetchPointer: self freeChunkNextIndex ofObject: freeChunk.
- prev = 0
- ifTrue: "freeChunk is a treeNode"
- [next = 0
- ifTrue: "remove it from the tree"
- [self unlinkSolitaryFreeTreeNode: freeChunk]
- ifFalse: "replace freeChunk by its next node."
- [self unlinkFreeTreeNode: freeChunk withSiblings: next]]
- ifFalse: "freeChunk is a list node; simple"
- [self storePointer: self freeChunkNextIndex ofFreeChunk: prev withValue: next.
- next ~= 0 ifTrue:
- [self storePointer: self freeChunkPrevIndex ofFreeChunk: next withValue: prev]]!
Item was changed:
----- Method: SpurMemoryManager>>exactFitCompact (in category 'compaction') -----
exactFitCompact
+ "Compact all of memory using exact-fit, assuming free space is sorted
+ and that the highest objects are recorded in highestObjects."
- "Compact all of memory using exact-fit.
- Sort free space, find the first free chunk. Scan objects following
- the first free chunk and for each, look for a free chunk of the right
- size, copy the object's contents into the free chunk, and forward
- the object to its new, lower location. Answer the first object that
- failed to fit (for bestFitCompacts convenience), if there's free space."
| firstFailedFit |
- self sortFreeSpace.
firstFailedFit := 0.
totalFreeOldSpace = 0 ifTrue: [^0].
self allOldSpaceObjectsFrom: self lowestFreeChunkAssumingSortedFreeSpace
do: [:o| | b |
((self isForwarded: o)
or: [self isPinned: o]) ifFalse:
[b := self bytesInObject: o.
+ (self allocateOldSpaceChunkOfExactlyBytes: b suchThat: [:f| f < o])
- (self allocateOldSpaceChunkOfExactlyBytes: b)
ifNil: [firstFailedFit = 0 ifTrue: [firstFailedFit := o]]
ifNotNil:
[:f|
self mem: f
cp: o
y: ((self hasOverflowHeader: o)
ifTrue: [b - self baseHeaderSize]
ifFalse: [b]).
(self isRemembered: o) ifTrue:
[scavenger remember: f].
self forward: o to: f]]].
self checkFreeSpace.
^firstFailedFit!
Item was changed:
----- Method: SpurMemoryManager>>freeTreeNodesDo: (in category 'free space') -----
freeTreeNodesDo: aBlock
"Enumerate all nodes in the free tree (in order, smaller to larger),
+ but *not* including the next nodes of the same size off each tree node.
- bit *not* including the next nodes of the same size off each tree node.
This is an iterative version so that the block argument can be
inlined by Slang. The trick to an iterative binary tree application is
to apply the function on the way back up when returning from a
+ particular direction, in this case up from the larger child.
+
+ N.B For the convenience of rebuildFreeTreeFromSortedFreeChunks
+ aBlock *MUST* answer the freeTreeNode it was invoked with, or
+ its replacement if it was replaced by aBlock."
- particular direction, in this case up from the larger child."
<inline: true>
| treeNode cameFrom |
treeNode := freeLists at: 0.
treeNode = 0 ifTrue:
[^self].
cameFrom := -1.
[| smallChild largeChild |
smallChild := self fetchPointer: self freeChunkSmallerIndex ofFreeChunk: treeNode.
largeChild := self fetchPointer: self freeChunkLargerIndex ofFreeChunk: treeNode.
"apply if the node has no children, or it has no large children and we're
returning from the small child, or we're returning from the large child."
((smallChild = 0 and: [largeChild = 0])
or: [largeChild = 0
ifTrue: [cameFrom = smallChild]
ifFalse: [cameFrom = largeChild]])
ifTrue:
+ [treeNode := aBlock value: treeNode.
- [aBlock value: treeNode.
"and since we've applied we must move on up"
cameFrom := treeNode.
treeNode := self fetchPointer: self freeChunkParentIndex ofFreeChunk: treeNode]
ifFalse:
[(smallChild ~= 0 and: [cameFrom ~= smallChild])
ifTrue:
[treeNode := smallChild]
ifFalse:
[self assert: largeChild ~= 0.
treeNode := largeChild].
cameFrom := -1].
treeNode ~= 0] whileTrue!
Item was added:
+ ----- Method: SpurMemoryManager>>freeUnmarkedObjectsNilUnmarkedWeaklingSlotsAndSortAndCoalesceFreeSpace (in category 'gc - global') -----
+ freeUnmarkedObjectsNilUnmarkedWeaklingSlotsAndSortAndCoalesceFreeSpace
+ "Sweep all of old space, freeing unmarked objects, nilling the unmarked slots of weaklings,
+ coalescing free chunks, and sorting free space. Small free chunks are sorted in address
+ order on each small list head. Large free chunks are sorted on the sortedFreeChunks list.
+ Record the highest N objects in highestObjects, for the first cycle of exactFitCompact.
+ Let the segmentManager mark which segments contain pinned objects via notePinned:"
+ | lastLargeFree highestObjectsWraps |
+ self checkFreeSpace.
+ scavenger forgetUnmarkedRememberedObjects.
+ segmentManager prepareForGlobalSweep."for notePinned:"
+ "for sorting free space throw away the list heads, rebuilding them for small free chunks below."
+ self resetFreeListHeads.
+ highestObjects
+ top: freeStart;
+ start: freeStart;
+ limit: scavenger eden limit.
+ highestObjectsWraps := 0.
+ self assert: highestObjects limit - highestObjects start // self wordSize >= 1024.
+ sortedFreeChunks := lastLargeFree := 0.
+ self allOldSpaceEntitiesForCoalescingDo:
+ [:o|
+ (self isMarked: o)
+ ifTrue:
+ [self setIsMarkedOf: o to: false.
+ ((self isWeakNonImm: o)
+ and: [self nilUnmarkedWeaklingSlots: o]) ifTrue:
+ [coInterpreter signalFinalization: o].
+ (self isPinned: o) ifTrue:
+ [segmentManager notePinned: o].
+ lastLargeFree ~= 0 ifTrue:
+ [self longAt: highestObjects top put: o.
+ (highestObjects top: (highestObjects top + self wordSize)) >= highestObjects limit ifTrue:
+ [highestObjects top: highestObjects start.
+ highestObjectsWraps := highestObjectsWraps + 1]]]
+ ifFalse: "unmarked; two cases, an unreachable object or a free chunk."
+ [| here next |
+ self assert: (self isRemembered: o) not. "scavenger should have cleared this above"
+ here := o.
+ next := self objectAfter: here limit: endOfMemory.
+ (self isMarked: next) ifFalse: "coalescing; rare case"
+ [self assert: (self isRemembered: o) not.
+ [statCoalesces := statCoalesces + 1.
+ here := self coalesce: here and: next.
+ next := self objectAfter: here limit: endOfMemory.
+ next = endOfMemory or: [self isMarked: next]] whileFalse].
+ (self isLargeFreeObject: here)
+ ifTrue:
+ [lastLargeFree = 0
+ ifTrue: [sortedFreeChunks := here]
+ ifFalse:
+ [self setFree: here.
+ self storePointer: self freeChunkNextAddressIndex ofFreeChunk: lastLargeFree withValue: here].
+ lastLargeFree := here]
+ ifFalse:
+ [self freeSmallObject: here]]].
+ lastLargeFree ~= 0 ifTrue:
+ [self storePointer: self freeChunkNextAddressIndex ofFreeChunk: lastLargeFree withValue: 0].
+ totalFreeOldSpace := self reverseSmallListHeads.
+ totalFreeOldSpace := totalFreeOldSpace + self rebuildFreeTreeFromSortedFreeChunks.
+ self checkFreeSpace.
+ self touch: highestObjectsWraps!
Item was removed:
- ----- Method: SpurMemoryManager>>freeUnmarkedObjectsNilUnmarkedWeaklingSlotsAndSortAndCoallesceFreeSpace (in category 'gc - global') -----
- freeUnmarkedObjectsNilUnmarkedWeaklingSlotsAndSortAndCoallesceFreeSpace
- "Sweep all of old space, freeing unmarked objects, nilling the unmarked slots of weaklings,
- coallescing free chunks, and sorting free space. Small free chunks are sorted in address
- order on each small list head. Large free chunks are sorted on the sortedFreeChunks list.
- Record the highest N objects in highestObjects, for the first cycle of exactFitCompact.
- Let the segmentManager mark which segments contain pinned objects via notePinned:"
- | lastLargeFree highestObjectsWraps |
- self checkFreeSpace.
- scavenger forgetUnmarkedRememberedObjects.
- segmentManager prepareForGlobalSweep."for notePinned:"
- "for sorting free space throw away the list heads, rebuilding them for small free chunks below."
- self resetFreeListHeads.
- highestObjects
- top: freeStart;
- start: freeStart;
- limit: scavenger eden limit.
- highestObjectsWraps := 0.
- self assert: highestObjects limit - highestObjects start // self wordSize >= 1024.
- sortedFreeChunks := lastLargeFree := 0.
- self allOldSpaceEntitiesForCoallescingDo:
- [:o|
- (self isMarked: o)
- ifTrue:
- [self setIsMarkedOf: o to: false.
- ((self isWeakNonImm: o)
- and: [self nilUnmarkedWeaklingSlots: o]) ifTrue:
- [coInterpreter signalFinalization: o].
- (self isPinned: o) ifTrue:
- [segmentManager notePinned: o].
- lastLargeFree ~= 0 ifTrue:
- [self longAt: highestObjects top put: o.
- (highestObjects top: (highestObjects top + self wordSize)) >= highestObjects limit ifTrue:
- [highestObjects top: highestObjects start.
- highestObjectsWraps := highestObjectsWraps + 1]]]
- ifFalse: "unmarked; two cases, an unreachable object or a free chunk."
- [| here next |
- self assert: (self isRemembered: o) not. "scavenger should have cleared this above"
- here := o.
- next := self objectAfter: here limit: endOfMemory.
- (self isMarked: next) ifFalse: "coallescing; rare case"
- [self assert: (self isRemembered: o) not.
- [statCoallesces := statCoallesces + 1.
- here := self coallesce: here and: next.
- next := self objectAfter: here limit: endOfMemory.
- next = endOfMemory or: [self isMarked: next]] whileFalse].
- (self isLargeFreeObject: here)
- ifTrue:
- [lastLargeFree = 0
- ifTrue: [sortedFreeChunks := here]
- ifFalse:
- [self setFree: here.
- self storePointer: self freeChunkNextAddressIndex ofFreeChunk: lastLargeFree withValue: here].
- lastLargeFree := here]
- ifFalse:
- [self freeSmallObject: here]]].
- lastLargeFree ~= 0 ifTrue:
- [self storePointer: self freeChunkNextAddressIndex ofFreeChunk: lastLargeFree withValue: 0].
- totalFreeOldSpace := self reverseSmallListHeads.
- totalFreeOldSpace := totalFreeOldSpace + self rebuildFreeTreeFromSortedFreeChunks.
- self checkFreeSpace.
- self touch: highestObjectsWraps!
Item was changed:
----- Method: SpurMemoryManager>>globalGarbageCollect (in category 'gc - global') -----
globalGarbageCollect
self markObjects.
+ self freeUnmarkedObjectsNilUnmarkedWeaklingSlotsAndSortAndCoalesceFreeSpace.
+ self bestFitCompact!
- self freeUnmarkedObjectsNilUnmarkedWeaklingSlotsAndSortAndCoallesceFreeSpace.
- self exactFitCompact!
Item was added:
+ ----- Method: SpurMemoryManager>>inFreeTreeReplace:with: (in category 'free space') -----
+ inFreeTreeReplace: treeNode with: newNode
+ | relative |
+ "copy parent, smaller, larger"
+ self freeChunkParentIndex to: self freeChunkLargerIndex do:
+ [:i|
+ self storePointer: i ofFreeChunk: newNode withValue: (self fetchPointer: i ofObject: treeNode)].
+ "replace link from parent to treeNode with link to newNode."
+ relative := self fetchPointer: self freeChunkParentIndex ofObject: treeNode.
+ relative = 0
+ ifTrue:
+ [freeLists at: 0 put: newNode]
+ ifFalse:
+ [self storePointer: (treeNode = (self fetchPointer: self freeChunkSmallerIndex ofObject: relative)
+ ifTrue: [self freeChunkSmallerIndex]
+ ifFalse: [self freeChunkLargerIndex])
+ ofFreeChunk: relative
+ withValue: newNode].
+ "replace parent links in children"
+ self freeChunkSmallerIndex to: self freeChunkLargerIndex do:
+ [:i|
+ relative := self fetchPointer: i ofObject: treeNode.
+ relative ~= 0 ifTrue:
+ [self storePointer: self freeChunkParentIndex ofFreeChunk: relative withValue: newNode]]!
Item was changed:
----- Method: SpurMemoryManager>>initialize (in category 'initialization') -----
initialize
"We can put all initializations that set something to 0 or to false here.
In C all global variables are initialized to 0, and 0 is false."
remapBuffer := Array new: RemapBufferSize.
remapBufferCount := extraRootCount := 0. "see below"
freeListsMask := totalFreeOldSpace := lowSpaceThreshold := 0.
checkForLeaks := 0.
needGCFlag := signalLowSpace := scavengeInProgress := marking := false.
becomeEffectsFlags := 0.
statScavenges := statIncrGCs := statFullGCs := 0.
statScavengeGCUsecs := statIncrGCUsecs := statFullGCUsecs := statGCEndUsecs := 0.
statSGCDeltaUsecs := statIGCDeltaUsecs := statFGCDeltaUsecs := 0.
statGrowMemory := statShrinkMemory := statRootTableCount := statTenures := statSurvivorCount := 0.
statRootTableOverflows := statSweepCount := statMarkCount := statSpecialMarkCount := statMkFwdCount := 0.
+ statCoalesces := 0.
- statCoallesces := 0.
"We can initialize things that are allocated but are lazily initialized."
unscannedEphemerons := SpurContiguousObjStack new.
highestObjects := SpurContiguousObjStack new.
"we can initialize things that are virtual in C."
scavenger := SpurGenerationScavengerSimulator new manager: self; yourself.
segmentManager := SpurSegmentManager new manager: self; yourself.
"We can also initialize here anything that is only for simulation."
heapMap := self wordSize = 4 ifTrue: [CogCheck32BitHeapMap new].
"N.B. We *don't* initialize extraRoots because we don't simulate it."!
Item was changed:
----- Method: SpurMemoryManager>>rebuildFreeTreeFromSortedFreeChunks (in category 'free space') -----
rebuildFreeTreeFromSortedFreeChunks
+ "post sweep and pre compact, rebuild the large free chunk tree from the
+ sortedFreeChunks list, such that the lists are ordered from low to high address."
- "post sweep and pre compact, rebuild the large
- free chunk tree from the sortedFreeChunks list."
| freeChunk bytes totalBytes |
+ "first add all the chunks to the tree. This will result in almost address-sorted lists.
+ We will need to reorder the lists."
freeChunk := sortedFreeChunks.
totalBytes := 0.
[freeChunk ~= 0] whileTrue:
[bytes := self bytesInObject: freeChunk.
totalBytes := totalBytes + bytes.
self addToFreeTree: freeChunk bytes: bytes.
freeChunk := self fetchPointer: self freeChunkNextAddressIndex
ofObject: freeChunk].
+ "now reorder the lists to ensure they're in address order, apart from the list head, which should be highest."
+ self freeTreeNodesDo:
+ [:treeNode| | newTreeNode |
+ newTreeNode := self reorderReversedTreeList: treeNode.
+ newTreeNode].
^totalBytes!
Item was added:
+ ----- Method: SpurMemoryManager>>reorderReversedTreeList: (in category 'free space') -----
+ reorderReversedTreeList: treeNode
+ "Once the freeTree has been rebuilt from the sortedFreeChunks list
+ each list will be in a weird order, the list in reverse order, high to low,
+ but the tree node, because it is inserted first, will be the lowest address.
+ Reverse the list so it is sorted low to high, but make the highest address
+ node the first, as this will be allocated from last."
+ | first next node prev |
+ "first becomes the new head, as this is the last one we want to allocate and we allocate from the list first."
+ first := self fetchPointer: self freeChunkNextIndex ofObject: treeNode.
+ "no next node, so no change"
+ first = 0 ifTrue:
+ [^treeNode].
+ node := self fetchPointer: self freeChunkNextIndex ofObject: first.
+ self storePointer: self freeChunkNextIndex ofFreeChunk: first withValue: treeNode.
+ self inFreeTreeReplace: treeNode with: first.
+ prev := 0.
+ [node ~= 0] whileTrue:
+ [next := self fetchPointer: self freeChunkNextIndex ofObject: node.
+ self storePointer: self freeChunkNextIndex ofFreeChunk: node withValue: prev.
+ prev := node.
+ node := next].
+ self storePointer: self freeChunkNextIndex ofFreeChunk: treeNode withValue: prev.
+ ^first!
Item was changed:
----- Method: SpurMemoryManager>>reverseSmallListHeads (in category 'free space') -----
reverseSmallListHeads
+ "After freeUnmarkedObjectsNilUnmarkedWeaklingSlotsAndSortAndCoalesceFreeSpace
- "After freeUnmarkedObjectsNilUnmarkedWeaklingSlotsAndSortAndCoallesceFreeSpace
all small free chunks will be on the free lists in reverse address order. Reverse each list,
summing the ammount of space. Answer the sum of bytes of free space on these small lists."
| total |
total := 0.
freeListsMask := 0.
1 to: self numFreeLists - 1 do:
[:i| | bytes node prev next |
bytes := i * self allocationUnit.
node := freeLists at: i.
node ~= 0 ifTrue:
[self assert: (self bytesInObject: node) = bytes.
freeListsMask := freeListsMask + (1 << i).
prev := 0.
[node ~= 0] whileTrue:
[next := self fetchPointer: self freeChunkNextIndex ofObject: node.
self storePointer: self freeChunkNextIndex ofFreeChunk: node withValue: prev.
prev := node.
node := next.
total := total + bytes].
freeLists at: i put: prev]].
^total!
Item was changed:
----- Method: SpurMemoryManager>>totalFreeListBytes (in category 'free space') -----
totalFreeListBytes
| totalFreeBytes bytesInChunk listNode |
totalFreeBytes := 0.
1 to: self numFreeLists - 1 do:
[:i|
bytesInChunk := i * self allocationUnit.
listNode := freeLists at: i.
[listNode ~= 0] whileTrue:
[totalFreeBytes := totalFreeBytes + bytesInChunk.
self assert: (self isValidFreeObject: listNode).
self assert: bytesInChunk = (self bytesInObject: listNode).
listNode := self fetchPointer: self freeChunkNextIndex ofFreeChunk: listNode]].
self freeTreeNodesDo:
[:treeNode|
bytesInChunk := self bytesInObject: treeNode.
self assert: bytesInChunk / self allocationUnit >= self numFreeLists.
listNode := treeNode.
[listNode ~= 0] whileTrue:
["self printFreeChunk: listNode"
self assert: (self isValidFreeObject: listNode).
totalFreeBytes := totalFreeBytes + bytesInChunk.
self assert: bytesInChunk = (self bytesInObject: listNode).
+ listNode := self fetchPointer: self freeChunkNextIndex ofFreeChunk: listNode].
+ treeNode].
- listNode := self fetchPointer: self freeChunkNextIndex ofFreeChunk: listNode]].
^totalFreeBytes!
Item was changed:
CogClass subclass: #SpurSegmentManager
instanceVariableNames: 'manager numSegments numSegInfos segments firstSegmentSize canSwizzle sweepIndex preferredPinningSegment'
classVariableNames: ''
poolDictionaries: ''
category: 'VMMaker-SpurMemoryManager'!
+ !SpurSegmentManager commentStamp: 'eem 10/21/2013 13:14' prior: 0!
- !SpurSegmentManager commentStamp: 'eem 10/6/2013 10:32' prior: 0!
Instances of SpurSegmentManager manage oldSpace, which is organized as a sequence of segments. Segments can be obtained from the operating system and returned to the operating system when empty and shrinkage is required. Segments are kept invisible from the SpurMemoryManager by using "bridge" objects, "fake" pinned objects to bridge the gaps between segments. A pinned object header occupies the last 16 bytes of each segment, and the pinned object's size is the distance to the start of the next segment. So when the memory manager enumerates objects it skips over these bridges and memory appears linear. The constraint is that segments obtained from the operating system must be at a higher address than the first segment. The maximum size of large objects, being an overflow slot size, should be big enough to bridge the gaps, because in 32-bits the maximum size is 2^32 slots. In 64-bits the maximum size of large objects is 2^56 slots, or 2^59 bits, which we hope will suffice.
When an image is written to a snapshot file the second word of the header of the bridge at the end of each segment is replaced by the size of the following segment, the segments are written to the file, and the second word of each bridge is restored. Hence the length of each segment is derived from the bridge at the end of the preceeding segment. The length of the first segment is stored in the image header as firstSegmentBytes. The start of each segment is also derived from the bridge as a delta from the start of the previous segment. The start of The first segment is stored in the image header as startOfMemory.
+ On load all segments are read into one single segment, eliminating the bridge objects, and computing the swizzle distance for each segment, based on where the segments were in memory when the image file was written, and where the coalesced segment ends up on load. Then the segment is traversed, swizzling pointers by selecting the relevant swizzle for each oop's segment.
- On load all segments are read into one single segment, eliminating the bridge objects, and computing the swizzle distance for each segment, based on where the segments were in memory when the image file was written, and where the coallesced segment ends up on load. Then the segment is traversed, swizzling pointers by selecting the relevant swizzle for each oop's segment.
Instance Variables
numSegments: <Integer>
segments: <Array of SpurSegmentInfo>
manager: <SpurMemoryManager>
numSegments
- the number of segments
segments
- the start addresses, lengths and offsets to adjust oops on image load, for each segment
manager
- the SpurMemoryManager whose oldSpace is managed (simulation only).!
Item was changed:
----- Method: SpurSegmentManager>>collapseSegmentsPostSwizzle (in category 'snapshot') -----
collapseSegmentsPostSwizzle
"The image has been loaded, old segments reconstructed, and the
loaded image swizzled into a single contiguous segment. Collapse
the segments intio one."
| bridge |
canSwizzle := false.
firstSegmentSize ifNil: "true when used by SpurBootstrap to transform an image"
[^self].
numSegments := 1.
(segments at: 0)
segStart: manager newSpaceLimit;
segSize: manager endOfMemory - manager newSpaceLimit.
+ "finally plant a bridge at the end of the coalesced segment and cut back the
- "finally plant a bridge at the end of the coallesced segment and cut back the
manager's notion of the end of memory to immediately before the bridge."
bridge := manager endOfMemory - manager bridgeSize.
self assert: bridge = ((segments at: 0) segStart
+ (segments at: 0) segSize
- (manager bridgeSize)).
manager
initSegmentBridgeWithBytes: manager bridgeSize at: bridge;
setEndOfMemory: bridge!
Item was changed:
----- Method: TAssignmentNode>>emitCCodeOn:level:generator: (in category 'C code generation') -----
emitCCodeOn: aStream level: level generator: aCodeGen
expression isSwitch ifTrue:
[^expression emitCCodeOn: aStream addToEndOfCases: self level: level generator: aCodeGen].
expression isLiteralArrayDeclaration ifTrue:
[^self emitLiteralArrayDeclarationOn: aStream level: level generator: aCodeGen].
+ (expression isSend and: [expression isValueExpansion]) ifTrue:
+ [^self emitValueExpansionOn: aStream level: level generator: aCodeGen].
variable emitCCodeOn: aStream level: level generator: aCodeGen.
self isVariableUpdatingAssignment
ifTrue:
[aStream
space;
nextPutAll: expression selector; "+ or -"
nextPut: $=;
space.
expression args first emitCCodeAsArgumentOn: aStream level: level generator: aCodeGen]
ifFalse:
[aStream space; nextPut: $=; space.
expression emitCCodeAsArgumentOn: aStream level: level generator: aCodeGen]!
Item was added:
+ ----- Method: TAssignmentNode>>emitValueExpansionOn:level:generator: (in category 'C code generation') -----
+ emitValueExpansionOn: aStream level: level generator: aCodeGen
+ | stmtList lastStmt copy |
+ self assert: (expression isSend and: [expression isValueExpansion]).
+ stmtList := expression receiver.
+ lastStmt := stmtList statements last.
+ lastStmt = variable ifTrue:
+ [^expression emitCCodeOn: aStream level: level generator: aCodeGen].
+ copy := stmtList copy.
+ copy statements
+ at: stmtList statements size
+ put: (TAssignmentNode new
+ setVariable: variable
+ expression: lastStmt).
+ (TSendNode new
+ setSelector: expression selector
+ receiver: copy
+ arguments: expression args)
+ emitCCodeOn: aStream level: level generator: aCodeGen.!
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.471.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.471
Author: eem
Time: 21 October 2013, 10:09:42.395 am
UUID: 51970cea-207a-46a1-85a3-b1296656e2df
Ancestors: VMMaker.oscog-eem.470
Implement rebuilding the free tree after sweep. Refactor
addToFreeList:bytes: to extract addToFreeTree:bytes:.
Fix reverseSmallListHeads.
Use the iterative freeTreeNodesDo: for totalFreeListBytes.
Add a containsPinned flag to SpurSegmentInfo and update it in
sweep. Also add a preferredPinningSegment to the seg mgr for
clustering pinned objects, when we get to it.
=============== Diff against VMMaker.oscog-eem.470 ===============
Item was changed:
CogClass subclass: #SpurMemoryManager
(excessive size, no diff calculated)
Item was changed:
----- Method: SpurMemoryManager>>addToFreeList:bytes: (in category 'free space') -----
addToFreeList: freeChunk bytes: chunkBytes
"Add freeChunk to the relevant freeList.
For the benefit of sortedFreeObject:, if freeChunk is large, answer the treeNode it
is added to, if it is added to the next list of a freeTreeNode, otherwise answer 0."
+ | index |
- | childBytes parent child index |
"coInterpreter transcript ensureCr. coInterpreter print: 'freeing '. self printFreeChunk: freeChunk."
self assert: (self isFreeObject: freeChunk).
self assert: chunkBytes = (self bytesInObject: freeChunk).
index := chunkBytes / self allocationUnit.
index < self numFreeLists ifTrue:
[self storePointer: self freeChunkNextIndex ofFreeChunk: freeChunk withValue: (freeLists at: index).
freeLists at: index put: freeChunk.
freeListsMask := freeListsMask bitOr: 1 << index.
^0].
+ ^self addToFreeTree: freeChunk bytes: chunkBytes!
- self
- storePointer: self freeChunkNextIndex ofFreeChunk: freeChunk withValue: 0;
- storePointer: self freeChunkParentIndex ofFreeChunk: freeChunk withValue: 0;
- storePointer: self freeChunkSmallerIndex ofFreeChunk: freeChunk withValue: 0;
- storePointer: self freeChunkLargerIndex ofFreeChunk: freeChunk withValue: 0.
- "Large chunk list organized as a tree, each node of which is a list of chunks of the same size.
- Beneath the node are smaller and larger blocks."
- parent := 0.
- child := freeLists at: 0.
- [child ~= 0] whileTrue:
- [childBytes := self bytesInObject: child.
- childBytes = chunkBytes ifTrue: "size match; add to list at node."
- [self storePointer: self freeChunkNextIndex
- ofFreeChunk: freeChunk
- withValue: (self fetchPointer: self freeChunkNextIndex ofObject: child);
- storePointer: self freeChunkNextIndex
- ofFreeChunk: child
- withValue: freeChunk.
- ^child].
- "walk down the tree"
- parent := child.
- child := self fetchPointer: (childBytes > chunkBytes
- ifTrue: [self freeChunkSmallerIndex]
- ifFalse: [self freeChunkLargerIndex])
- ofObject: child].
- parent = 0 ifTrue:
- [self assert: (freeLists at: 0) = 0.
- freeLists at: 0 put: freeChunk.
- freeListsMask := freeListsMask bitOr: 1.
- ^0].
- "insert in tree"
- self storePointer: self freeChunkParentIndex
- ofFreeChunk: freeChunk
- withValue: parent.
- self storePointer: (childBytes > chunkBytes
- ifTrue: [self freeChunkSmallerIndex]
- ifFalse: [self freeChunkLargerIndex])
- ofFreeChunk: parent
- withValue: freeChunk.
- ^0!
Item was added:
+ ----- Method: SpurMemoryManager>>addToFreeTree:bytes: (in category 'free space') -----
+ addToFreeTree: freeChunk bytes: chunkBytes
+ "Add freeChunk to the large free chunk tree.
+ For the benefit of sortedFreeObject:, answer the treeNode it is added
+ to, if it is added to the next list of a freeTreeNode, otherwise answer 0."
+ | childBytes parent child |
+ self assert: chunkBytes = (self bytesInObject: freeChunk).
+ self assert: chunkBytes / self allocationUnit >= self numFreeLists.
+
+ self
+ storePointer: self freeChunkNextIndex ofFreeChunk: freeChunk withValue: 0;
+ storePointer: self freeChunkParentIndex ofFreeChunk: freeChunk withValue: 0;
+ storePointer: self freeChunkSmallerIndex ofFreeChunk: freeChunk withValue: 0;
+ storePointer: self freeChunkLargerIndex ofFreeChunk: freeChunk withValue: 0.
+ "Large chunk list organized as a tree, each node of which is a list of chunks of the same size.
+ Beneath the node are smaller and larger blocks."
+ parent := 0.
+ child := freeLists at: 0.
+ [child ~= 0] whileTrue:
+ [childBytes := self bytesInObject: child.
+ childBytes = chunkBytes ifTrue: "size match; add to list at node."
+ [self storePointer: self freeChunkNextIndex
+ ofFreeChunk: freeChunk
+ withValue: (self fetchPointer: self freeChunkNextIndex ofObject: child);
+ storePointer: self freeChunkNextIndex
+ ofFreeChunk: child
+ withValue: freeChunk.
+ ^child].
+ "walk down the tree"
+ parent := child.
+ child := self fetchPointer: (childBytes > chunkBytes
+ ifTrue: [self freeChunkSmallerIndex]
+ ifFalse: [self freeChunkLargerIndex])
+ ofObject: child].
+ parent = 0 ifTrue:
+ [self assert: (freeLists at: 0) = 0.
+ freeLists at: 0 put: freeChunk.
+ freeListsMask := freeListsMask bitOr: 1.
+ ^0].
+ "insert in tree"
+ self storePointer: self freeChunkParentIndex
+ ofFreeChunk: freeChunk
+ withValue: parent.
+ self storePointer: (childBytes > chunkBytes
+ ifTrue: [self freeChunkSmallerIndex]
+ ifFalse: [self freeChunkLargerIndex])
+ ofFreeChunk: parent
+ withValue: freeChunk.
+ ^0!
Item was removed:
- ----- Method: SpurMemoryManager>>bytesInFreeTree: (in category 'free space') -----
- bytesInFreeTree: freeNode
- | freeBytes bytesInObj listNode |
- freeNode = 0 ifTrue: [^0].
- freeBytes := 0.
- bytesInObj := self bytesInObject: freeNode.
- self assert: bytesInObj / self allocationUnit >= self numFreeLists.
- listNode := freeNode.
- [listNode ~= 0] whileTrue:
- ["self printFreeChunk: listNode"
- self assert: (self isValidFreeObject: listNode).
- freeBytes := freeBytes + bytesInObj.
- self assert: bytesInObj = (self bytesInObject: listNode).
- listNode := self fetchPointer: self freeChunkNextIndex ofFreeChunk: listNode].
- ^freeBytes
- + (self bytesInFreeTree: (self fetchPointer: self freeChunkSmallerIndex ofFreeChunk: freeNode))
- + (self bytesInFreeTree: (self fetchPointer: self freeChunkLargerIndex ofFreeChunk: freeNode))!
Item was changed:
----- Method: SpurMemoryManager>>freeUnmarkedObjectsNilUnmarkedWeaklingSlotsAndSortAndCoallesceFreeSpace (in category 'gc - global') -----
freeUnmarkedObjectsNilUnmarkedWeaklingSlotsAndSortAndCoallesceFreeSpace
"Sweep all of old space, freeing unmarked objects, nilling the unmarked slots of weaklings,
coallescing free chunks, and sorting free space. Small free chunks are sorted in address
+ order on each small list head. Large free chunks are sorted on the sortedFreeChunks list.
+ Record the highest N objects in highestObjects, for the first cycle of exactFitCompact.
+ Let the segmentManager mark which segments contain pinned objects via notePinned:"
+ | lastLargeFree highestObjectsWraps |
- order on each small list head. Large free chunks are sorted on the sortedFreeChunks list."
- | lastLargeFree |
self checkFreeSpace.
scavenger forgetUnmarkedRememberedObjects.
+ segmentManager prepareForGlobalSweep."for notePinned:"
- self checkFreeSpace.
"for sorting free space throw away the list heads, rebuilding them for small free chunks below."
self resetFreeListHeads.
+ highestObjects
+ top: freeStart;
+ start: freeStart;
+ limit: scavenger eden limit.
+ highestObjectsWraps := 0.
+ self assert: highestObjects limit - highestObjects start // self wordSize >= 1024.
sortedFreeChunks := lastLargeFree := 0.
self allOldSpaceEntitiesForCoallescingDo:
[:o|
(self isMarked: o)
ifTrue:
[self setIsMarkedOf: o to: false.
((self isWeakNonImm: o)
and: [self nilUnmarkedWeaklingSlots: o]) ifTrue:
+ [coInterpreter signalFinalization: o].
+ (self isPinned: o) ifTrue:
+ [segmentManager notePinned: o].
+ lastLargeFree ~= 0 ifTrue:
+ [self longAt: highestObjects top put: o.
+ (highestObjects top: (highestObjects top + self wordSize)) >= highestObjects limit ifTrue:
+ [highestObjects top: highestObjects start.
+ highestObjectsWraps := highestObjectsWraps + 1]]]
- [coInterpreter signalFinalization: o]]
ifFalse: "unmarked; two cases, an unreachable object or a free chunk."
[| here next |
self assert: (self isRemembered: o) not. "scavenger should have cleared this above"
here := o.
next := self objectAfter: here limit: endOfMemory.
(self isMarked: next) ifFalse: "coallescing; rare case"
[self assert: (self isRemembered: o) not.
[statCoallesces := statCoallesces + 1.
here := self coallesce: here and: next.
next := self objectAfter: here limit: endOfMemory.
next = endOfMemory or: [self isMarked: next]] whileFalse].
(self isLargeFreeObject: here)
ifTrue:
[lastLargeFree = 0
ifTrue: [sortedFreeChunks := here]
ifFalse:
[self setFree: here.
self storePointer: self freeChunkNextAddressIndex ofFreeChunk: lastLargeFree withValue: here].
lastLargeFree := here]
ifFalse:
[self freeSmallObject: here]]].
lastLargeFree ~= 0 ifTrue:
[self storePointer: self freeChunkNextAddressIndex ofFreeChunk: lastLargeFree withValue: 0].
totalFreeOldSpace := self reverseSmallListHeads.
totalFreeOldSpace := totalFreeOldSpace + self rebuildFreeTreeFromSortedFreeChunks.
+ self checkFreeSpace.
+ self touch: highestObjectsWraps!
- self checkFreeSpace!
Item was changed:
----- Method: SpurMemoryManager>>initialize (in category 'initialization') -----
initialize
"We can put all initializations that set something to 0 or to false here.
In C all global variables are initialized to 0, and 0 is false."
remapBuffer := Array new: RemapBufferSize.
remapBufferCount := extraRootCount := 0. "see below"
freeListsMask := totalFreeOldSpace := lowSpaceThreshold := 0.
checkForLeaks := 0.
needGCFlag := signalLowSpace := scavengeInProgress := marking := false.
becomeEffectsFlags := 0.
statScavenges := statIncrGCs := statFullGCs := 0.
statScavengeGCUsecs := statIncrGCUsecs := statFullGCUsecs := statGCEndUsecs := 0.
statSGCDeltaUsecs := statIGCDeltaUsecs := statFGCDeltaUsecs := 0.
statGrowMemory := statShrinkMemory := statRootTableCount := statTenures := statSurvivorCount := 0.
statRootTableOverflows := statSweepCount := statMarkCount := statSpecialMarkCount := statMkFwdCount := 0.
statCoallesces := 0.
"We can initialize things that are allocated but are lazily initialized."
unscannedEphemerons := SpurContiguousObjStack new.
+ highestObjects := SpurContiguousObjStack new.
"we can initialize things that are virtual in C."
scavenger := SpurGenerationScavengerSimulator new manager: self; yourself.
segmentManager := SpurSegmentManager new manager: self; yourself.
"We can also initialize here anything that is only for simulation."
heapMap := self wordSize = 4 ifTrue: [CogCheck32BitHeapMap new].
"N.B. We *don't* initialize extraRoots because we don't simulate it."!
Item was added:
+ ----- Method: SpurMemoryManager>>rebuildFreeTreeFromSortedFreeChunks (in category 'free space') -----
+ rebuildFreeTreeFromSortedFreeChunks
+ "post sweep and pre compact, rebuild the large
+ free chunk tree from the sortedFreeChunks list."
+ | freeChunk bytes totalBytes |
+ freeChunk := sortedFreeChunks.
+ totalBytes := 0.
+ [freeChunk ~= 0] whileTrue:
+ [bytes := self bytesInObject: freeChunk.
+ totalBytes := totalBytes + bytes.
+ self addToFreeTree: freeChunk bytes: bytes.
+ freeChunk := self fetchPointer: self freeChunkNextAddressIndex
+ ofObject: freeChunk].
+ ^totalBytes!
Item was removed:
- ----- Method: SpurMemoryManager>>resetSmallFreeListHeads (in category 'free space') -----
- resetSmallFreeListHeads
- 1 to: self numFreeLists - 1 do:
- [:i| freeLists at: i put: 0]!
Item was changed:
----- Method: SpurMemoryManager>>reverseSmallListHeads (in category 'free space') -----
reverseSmallListHeads
"After freeUnmarkedObjectsNilUnmarkedWeaklingSlotsAndSortAndCoallesceFreeSpace
all small free chunks will be on the free lists in reverse address order. Reverse each list,
summing the ammount of space. Answer the sum of bytes of free space on these small lists."
| total |
total := 0.
+ freeListsMask := 0.
1 to: self numFreeLists - 1 do:
[:i| | bytes node prev next |
bytes := i * self allocationUnit.
node := freeLists at: i.
+ node ~= 0 ifTrue:
+ [self assert: (self bytesInObject: node) = bytes.
+ freeListsMask := freeListsMask + (1 << i).
+ prev := 0.
+ [node ~= 0] whileTrue:
+ [next := self fetchPointer: self freeChunkNextIndex ofObject: node.
+ self storePointer: self freeChunkNextIndex ofFreeChunk: node withValue: prev.
+ prev := node.
+ node := next.
+ total := total + bytes].
+ freeLists at: i put: prev]].
- prev := 0.
- [node ~= 0] whileTrue:
- [next := self fetchPointer: self freeChunkNextIndex ofObject: node.
- self storePointer: self freeChunkNextIndex ofFreeChunk: node withValue: prev.
- prev := node.
- node := next.
- total := total + bytes].
- freeLists at: i put: node].
^total!
Item was changed:
----- Method: SpurMemoryManager>>totalFreeListBytes (in category 'free space') -----
totalFreeListBytes
+ | totalFreeBytes bytesInChunk listNode |
+ totalFreeBytes := 0.
- | freeBytes bytesInObj obj |
- freeBytes := 0.
1 to: self numFreeLists - 1 do:
[:i|
+ bytesInChunk := i * self allocationUnit.
+ listNode := freeLists at: i.
+ [listNode ~= 0] whileTrue:
+ [totalFreeBytes := totalFreeBytes + bytesInChunk.
+ self assert: (self isValidFreeObject: listNode).
+ self assert: bytesInChunk = (self bytesInObject: listNode).
+ listNode := self fetchPointer: self freeChunkNextIndex ofFreeChunk: listNode]].
+
+ self freeTreeNodesDo:
+ [:treeNode|
+ bytesInChunk := self bytesInObject: treeNode.
+ self assert: bytesInChunk / self allocationUnit >= self numFreeLists.
+ listNode := treeNode.
+ [listNode ~= 0] whileTrue:
+ ["self printFreeChunk: listNode"
+ self assert: (self isValidFreeObject: listNode).
+ totalFreeBytes := totalFreeBytes + bytesInChunk.
+ self assert: bytesInChunk = (self bytesInObject: listNode).
+ listNode := self fetchPointer: self freeChunkNextIndex ofFreeChunk: listNode]].
+ ^totalFreeBytes!
- bytesInObj := i * self allocationUnit.
- obj := freeLists at: i.
- [obj ~= 0] whileTrue:
- [freeBytes := freeBytes + bytesInObj.
- self assert: (self isValidFreeObject: obj).
- self assert: bytesInObj = (self bytesInObject: obj).
- obj := self fetchPointer: self freeChunkNextIndex ofFreeChunk: obj]].
- ^freeBytes + (self bytesInFreeTree: (freeLists at: 0))!
Item was changed:
VMStructType subclass: #SpurSegmentInfo
+ instanceVariableNames: 'segStart segSize swizzle containsPinned'
- instanceVariableNames: 'segStart segSize swizzle'
classVariableNames: ''
poolDictionaries: ''
category: 'VMMaker-SpurMemoryManager'!
Item was added:
+ ----- Method: SpurSegmentInfo>>containsPinned (in category 'accessing') -----
+ containsPinned
+ "Answer the value of containsPinned"
+
+ ^ containsPinned!
Item was added:
+ ----- Method: SpurSegmentInfo>>containsPinned: (in category 'accessing') -----
+ containsPinned: anObject
+ "Set the value of containsPinned"
+
+ ^containsPinned := anObject!
Item was added:
+ ----- Method: SpurSegmentInfo>>initialize (in category 'initialization') -----
+ initialize
+ segSize := segStart := swizzle := 0.
+ containsPinned := false!
Item was changed:
----- Method: SpurSegmentInfo>>printOn: (in category 'printing') -----
printOn: aStream
<doNotGenerate>
super printOn: aStream.
self class instVarNames do:
[:name| | iv |
iv := self instVarNamed: name.
aStream space; nextPutAll: name; space; print: iv.
+ (iv isInteger and: [iv ~= 0]) ifTrue:
- iv isInteger ifTrue:
[aStream nextPut: $/. iv storeOn: aStream base: 16]]!
Item was changed:
CogClass subclass: #SpurSegmentManager
+ instanceVariableNames: 'manager numSegments numSegInfos segments firstSegmentSize canSwizzle sweepIndex preferredPinningSegment'
- instanceVariableNames: 'manager numSegments numSegInfos segments firstSegmentSize canSwizzle'
classVariableNames: ''
poolDictionaries: ''
category: 'VMMaker-SpurMemoryManager'!
!SpurSegmentManager commentStamp: 'eem 10/6/2013 10:32' prior: 0!
Instances of SpurSegmentManager manage oldSpace, which is organized as a sequence of segments. Segments can be obtained from the operating system and returned to the operating system when empty and shrinkage is required. Segments are kept invisible from the SpurMemoryManager by using "bridge" objects, "fake" pinned objects to bridge the gaps between segments. A pinned object header occupies the last 16 bytes of each segment, and the pinned object's size is the distance to the start of the next segment. So when the memory manager enumerates objects it skips over these bridges and memory appears linear. The constraint is that segments obtained from the operating system must be at a higher address than the first segment. The maximum size of large objects, being an overflow slot size, should be big enough to bridge the gaps, because in 32-bits the maximum size is 2^32 slots. In 64-bits the maximum size of large objects is 2^56 slots, or 2^59 bits, which we hope will suffice.
When an image is written to a snapshot file the second word of the header of the bridge at the end of each segment is replaced by the size of the following segment, the segments are written to the file, and the second word of each bridge is restored. Hence the length of each segment is derived from the bridge at the end of the preceeding segment. The length of the first segment is stored in the image header as firstSegmentBytes. The start of each segment is also derived from the bridge as a delta from the start of the previous segment. The start of The first segment is stored in the image header as startOfMemory.
On load all segments are read into one single segment, eliminating the bridge objects, and computing the swizzle distance for each segment, based on where the segments were in memory when the image file was written, and where the coallesced segment ends up on load. Then the segment is traversed, swizzling pointers by selecting the relevant swizzle for each oop's segment.
Instance Variables
numSegments: <Integer>
segments: <Array of SpurSegmentInfo>
manager: <SpurMemoryManager>
numSegments
- the number of segments
segments
- the start addresses, lengths and offsets to adjust oops on image load, for each segment
manager
- the SpurMemoryManager whose oldSpace is managed (simulation only).!
Item was added:
+ ----- Method: SpurSegmentManager>>notePinned: (in category 'pinning') -----
+ notePinned: objOop
+ "Let the segmentManager mark which segments contain pinned objects"
+ self assert: (manager isPinned: objOop).
+ (manager isSegmentBridge: objOop) ifFalse:
+ [[(segments at: sweepIndex) segStart + (segments at: sweepIndex) segSize < objOop] whileTrue:
+ [sweepIndex := sweepIndex + 1].
+ (segments at: sweepIndex) containsPinned: true]!
Item was added:
+ ----- Method: SpurSegmentManager>>prepareForGlobalSweep (in category 'pinning') -----
+ prepareForGlobalSweep
+ "Let the segmentManager mark which segments contain pinned objects via notePinned:"
+ sweepIndex := 0.
+ 0 to: numSegments - 1 do:
+ [:i| (segments at: i) containsPinned: false]!