From bbcdf3500cdfdaa8257d6688eac85cbf3babf901 Mon Sep 17 00:00:00 2001 From: Pablo Tesone Date: Wed, 10 Jul 2024 11:07:44 +0200 Subject: [PATCH 1/2] Adding message when aborting --- src/debug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/debug.c b/src/debug.c index f46151c72b..be1afeaf6a 100644 --- a/src/debug.c +++ b/src/debug.c @@ -43,6 +43,7 @@ EXPORT(int) isLogDebug(){ void error(char *errorMessage){ logError(errorMessage); + logError("Aborting the execution of the VM"); printStatusAfterError(); abort(); } From 774543beb9e3f529a5c1f71e609935bfa6e65a80 Mon Sep 17 00:00:00 2001 From: Pablo Tesone Date: Wed, 10 Jul 2024 11:30:38 +0200 Subject: [PATCH 2/2] - Moving repeated simulator code to the Trait - Adding an origin message for handling the error of growing old space when a limit arrives --- .../VMMaker/InterpreterPrimitives.class.st | 13 +- .../VMMaker/Spur32BitMMLECoSimulator.class.st | 9 -- .../VMMaker/Spur32BitMMLESimulator.class.st | 9 -- .../VMMaker/Spur64BitMMLECoSimulator.class.st | 9 -- .../VMMaker/Spur64BitMMLESimulator.class.st | 9 -- .../VMMaker/SpurGenerationScavenger.class.st | 69 +++++---- .../VMMaker/SpurMemoryManager.class.st | 137 +++++++++++------- .../VMMaker/SpurSelectiveCompactor.class.st | 16 +- .../TVMSpurMemoryManagerSimulator.trait.st | 12 ++ smalltalksrc/VMMaker/VMRememberedSet.class.st | 64 ++++---- .../VMMakerTests/VMImageReadingTest.class.st | 37 +++-- .../VMSegmentsImageFormatTest.class.st | 56 ++++--- ...MSpurOldSpaceGarbageCollectorTest.class.st | 22 +-- 13 files changed, 261 insertions(+), 201 deletions(-) diff --git a/smalltalksrc/VMMaker/InterpreterPrimitives.class.st b/smalltalksrc/VMMaker/InterpreterPrimitives.class.st index 9b944eef20..63f50e6a74 100644 --- a/smalltalksrc/VMMaker/InterpreterPrimitives.class.st +++ b/smalltalksrc/VMMaker/InterpreterPrimitives.class.st @@ -2017,14 +2017,17 @@ InterpreterPrimitives >> primitiveGreaterThanLargeIntegers [ { #category : 'memory space primitives' } InterpreterPrimitives >> primitiveGrowMemoryByAtLeast [ + | ammount | ammount := self stackTop. - (objectMemory isIntegerObject: ammount) ifFalse: - [^self primitiveFailFor: PrimErrBadArgument]. - (objectMemory growOldSpaceByAtLeast: (objectMemory integerValueOf: ammount)) - ifNil: [self primitiveFailFor: PrimErrNoMemory] - ifNotNil: [:segSize| self pop: 2 thenPushInteger: segSize] + (objectMemory isIntegerObject: ammount) ifFalse: [ + ^ self primitiveFailFor: PrimErrBadArgument ]. + (objectMemory + growOldSpaceByAtLeast: (objectMemory integerValueOf: ammount) + callingOperation: 'primitiveGrowMemoryByAtLeast - requested by the image') + ifNil: [ self primitiveFailFor: PrimErrNoMemory ] + ifNotNil: [ :segSize | self pop: 2 thenPushInteger: segSize ] ] { #category : 'arithmetic integer primitives' } diff --git a/smalltalksrc/VMMaker/Spur32BitMMLECoSimulator.class.st b/smalltalksrc/VMMaker/Spur32BitMMLECoSimulator.class.st index 4fec252f7b..b80684ff0a 100644 --- a/smalltalksrc/VMMaker/Spur32BitMMLECoSimulator.class.st +++ b/smalltalksrc/VMMaker/Spur32BitMMLECoSimulator.class.st @@ -121,15 +121,6 @@ Spur32BitMMLECoSimulator >> globalGarbageCollect [ ^super globalGarbageCollect ] -{ #category : 'growing/shrinking memory' } -Spur32BitMMLECoSimulator >> growOldSpaceByAtLeast: minAmmount [ - "Attempt to grow memory by at least minAmmount. - Answer the size of the new segment, or nil if the attempt failed. - Override to not grow during the Spur image bootstrap." - ^bootstrapping ifFalse: - [super growOldSpaceByAtLeast: minAmmount] -] - { #category : 'memory access' } Spur32BitMMLECoSimulator >> halfWordHighInLong32: long32 [ "Used by Balloon" diff --git a/smalltalksrc/VMMaker/Spur32BitMMLESimulator.class.st b/smalltalksrc/VMMaker/Spur32BitMMLESimulator.class.st index f264f19829..90674e4a6c 100644 --- a/smalltalksrc/VMMaker/Spur32BitMMLESimulator.class.st +++ b/smalltalksrc/VMMaker/Spur32BitMMLESimulator.class.st @@ -110,15 +110,6 @@ Spur32BitMMLESimulator >> globalGarbageCollect [ ^super globalGarbageCollect ] -{ #category : 'growing/shrinking memory' } -Spur32BitMMLESimulator >> growOldSpaceByAtLeast: minAmmount [ - "Attempt to grow memory by at least minAmmount. - Answer the size of the new segment, or nil if the attempt failed. - Override to not grow during the Spur image bootstrap." - ^bootstrapping ifFalse: - [super growOldSpaceByAtLeast: minAmmount] -] - { #category : 'memory access' } Spur32BitMMLESimulator >> halfWordHighInLong32: long32 [ "Used by Balloon" diff --git a/smalltalksrc/VMMaker/Spur64BitMMLECoSimulator.class.st b/smalltalksrc/VMMaker/Spur64BitMMLECoSimulator.class.st index 4056a2a694..8ac23ede7e 100644 --- a/smalltalksrc/VMMaker/Spur64BitMMLECoSimulator.class.st +++ b/smalltalksrc/VMMaker/Spur64BitMMLECoSimulator.class.st @@ -115,15 +115,6 @@ Spur64BitMMLECoSimulator >> globalGarbageCollect [ ^super globalGarbageCollect ] -{ #category : 'growing/shrinking memory' } -Spur64BitMMLECoSimulator >> growOldSpaceByAtLeast: minAmmount [ - "Attempt to grow memory by at least minAmmount. - Answer the size of the new segment, or nil if the attempt failed. - Override to not grow during the Spur image bootstrap." - ^bootstrapping ifFalse: - [super growOldSpaceByAtLeast: minAmmount] -] - { #category : 'memory access' } Spur64BitMMLECoSimulator >> halfWordHighInLong32: long32 [ "Used by Balloon" diff --git a/smalltalksrc/VMMaker/Spur64BitMMLESimulator.class.st b/smalltalksrc/VMMaker/Spur64BitMMLESimulator.class.st index 0d2f50280c..05cf0892e9 100644 --- a/smalltalksrc/VMMaker/Spur64BitMMLESimulator.class.st +++ b/smalltalksrc/VMMaker/Spur64BitMMLESimulator.class.st @@ -98,15 +98,6 @@ Spur64BitMMLESimulator >> freeLists [ ^freeLists ] -{ #category : 'growing/shrinking memory' } -Spur64BitMMLESimulator >> growOldSpaceByAtLeast: minAmmount [ - "Attempt to grow memory by at least minAmmount. - Answer the size of the new segment, or nil if the attempt failed. - Override to not grow during the Spur image bootstrap." - ^bootstrapping ifFalse: - [super growOldSpaceByAtLeast: minAmmount] -] - { #category : 'memory access' } Spur64BitMMLESimulator >> halfWordHighInLong32: long32 [ "Used by Balloon" diff --git a/smalltalksrc/VMMaker/SpurGenerationScavenger.class.st b/smalltalksrc/VMMaker/SpurGenerationScavenger.class.st index 77ea0470b6..e2aa3f55b6 100644 --- a/smalltalksrc/VMMaker/SpurGenerationScavenger.class.st +++ b/smalltalksrc/VMMaker/SpurGenerationScavenger.class.st @@ -341,46 +341,59 @@ SpurGenerationScavenger >> copyToFutureSpace: survivor bytes: bytesInObject [ { #category : 'scavenger' } SpurGenerationScavenger >> copyToOldSpace: survivor bytes: bytesInObject format: formatOfSurvivor [ "Copy survivor to oldSpace. Answer the new oop of the object." - "Should be too infrequent to lower icache density of copyAndForward:" + + "Should be too infrequent to lower icache density of copyAndForward:" + + | nTenures startOfSurvivor newStart newOop growResult | - self assert: (formatOfSurvivor = (manager formatOf: survivor) - and: [((manager isMarked: survivor) not or: [tenureCriterion = MarkOnTenure]) - and: [tenureCriterion = TenureToShrinkRT - or: [(manager isPinned: survivor) not - and: [(manager isRemembered: survivor) not]]]]). + self assert: (formatOfSurvivor = (manager formatOf: survivor) and: [ + ((manager isMarked: survivor) not or: [ + tenureCriterion = MarkOnTenure ]) and: [ + tenureCriterion = TenureToShrinkRT or: [ + (manager isPinned: survivor) not and: [ + (manager isRemembered: survivor) not ] ] ] ]). nTenures := statTenures. startOfSurvivor := manager startOfObject: survivor. newStart := manager allocateOldSpaceChunkOfBytes: bytesInObject. - newStart ifNil: - [growResult := manager growOldSpaceByAtLeast: 0. "grow by growHeadroom" - newStart := manager allocateOldSpaceChunkOfBytes: bytesInObject. - newStart ifNil: - [ growResult - ifNil: [ self error: 'Could not allocate new object in the old space. It was not possible to allocate a new memory segment' ] - ifNotNil: [ self error: 'Could not allocate new object in the old space' ]]]. - + newStart ifNil: [ + growResult := manager + growOldSpaceByAtLeast: 0 + callingOperation: 'copying objects to OldSpace during GC'. "grow by growHeadroom" + newStart := manager allocateOldSpaceChunkOfBytes: bytesInObject. + newStart ifNil: [ + growResult + ifNil: [ + self error: + 'Could not allocate new object in the old space. It was not possible to allocate a new memory segment' ] + ifNotNil: [ + self error: 'Could not allocate new object in the old space' ] ] ]. + "manager checkFreeSpace." - manager memcpy: newStart asVoidPointer _: startOfSurvivor asVoidPointer _: bytesInObject. + manager + memcpy: newStart asVoidPointer + _: startOfSurvivor asVoidPointer + _: bytesInObject. newOop := newStart + (survivor - startOfSurvivor). - tenureCriterion >= (TenureToShrinkRT min: MarkOnTenure) ifTrue: - [tenureCriterion = TenureToShrinkRT ifTrue: - [manager rtRefCountOf: newOop put: 0]. - tenureCriterion = MarkOnTenure ifTrue: - [manager setIsMarkedOf: newOop to: true]]. + tenureCriterion >= (TenureToShrinkRT min: MarkOnTenure) ifTrue: [ + tenureCriterion = TenureToShrinkRT ifTrue: [ + manager rtRefCountOf: newOop put: 0 ]. + tenureCriterion = MarkOnTenure ifTrue: [ + manager setIsMarkedOf: newOop to: true ] ]. statTenures := nTenures + 1. - (manager isAnyPointerFormat: formatOfSurvivor) ifTrue: - ["A very quick and dirty scan to find young referents. If we misidentify bytes + (manager isAnyPointerFormat: formatOfSurvivor) ifTrue: [ "A very quick and dirty scan to find young referents. If we misidentify bytes in a CompiledMethod as young we don't care; it's unlikely, and a subsequent scan of the rt will filter the object out. But it's good to filter here because otherwise an attempt to shrink the RT may simply fill it up with new objects, and here the data is likely in the cache." - manager baseHeaderSize to: bytesInObject - (survivor - startOfSurvivor) - manager wordSize by: manager wordSize do: - [:p| | field | + manager baseHeaderSize to: + bytesInObject - (survivor - startOfSurvivor) - manager wordSize by: + manager wordSize do: [ :p | + | field | field := manager longAt: survivor + p. - (manager isReallyYoung: field) ifTrue: - [manager getFromOldSpaceRememberedSet remember: newOop. - ^newOop]]]. - ^newOop + (manager isReallyYoung: field) ifTrue: [ + manager getFromOldSpaceRememberedSet remember: newOop. + ^ newOop ] ] ]. + ^ newOop ] { #category : 'weakness and ephemerality' } diff --git a/smalltalksrc/VMMaker/SpurMemoryManager.class.st b/smalltalksrc/VMMaker/SpurMemoryManager.class.st index 36c72519dc..e7384f3096 100644 --- a/smalltalksrc/VMMaker/SpurMemoryManager.class.st +++ b/smalltalksrc/VMMaker/SpurMemoryManager.class.st @@ -4685,39 +4685,67 @@ SpurMemoryManager >> ensureRoomOnObjStackAt: objStackRootIndex [ ObjStackNextx. We don't want to shrink objStacks, since they're used in GC and its good to keep their memory around. So unused pages created by popping emptying pages are kept on the ObjStackFreex list." + | stackOrNil freeOrNewPage | - stackOrNil := self fetchPointer: objStackRootIndex ofObject: hiddenRootsObj. - (stackOrNil = nilObj - or: [(self fetchPointer: ObjStackTopx ofObject: stackOrNil) >= ObjStackLimit]) ifTrue: - [freeOrNewPage := stackOrNil = nilObj - ifTrue: [0] - ifFalse: [self fetchPointer: ObjStackFreex ofObject: stackOrNil]. - freeOrNewPage ~= 0 - ifTrue: "the free page list is always on the new page." - [self storePointer: ObjStackFreex ofObjStack: stackOrNil withValue: 0. - self assert: (marking not or: [self isMarked: freeOrNewPage])] - ifFalse: - [freeOrNewPage := self allocateSlotsInOldSpace: ObjStackPageSlots - format: self wordIndexableFormat - classIndex: self wordSizeClassIndexPun. - freeOrNewPage ifNil: - ["Allocate a new segment an retry. This is very uncommon. But it happened to me (Clement)." - self growOldSpaceByAtLeast: ObjStackPageSlots. - freeOrNewPage := self allocateSlotsInOldSpace: ObjStackPageSlots - format: self wordIndexableFormat - classIndex: self wordSizeClassIndexPun. - freeOrNewPage ifNil: [self error: 'no memory to allocate or extend obj stack']]. - self storePointer: ObjStackFreex ofObjStack: freeOrNewPage withValue: 0. - marking ifTrue: [self setIsMarkedOf: freeOrNewPage to: true]]. - self storePointer: ObjStackMyx ofObjStack: freeOrNewPage withValue: objStackRootIndex; - storePointer: ObjStackNextx ofObjStack: freeOrNewPage withValue: (stackOrNil = nilObj ifTrue: [0] ifFalse: [stackOrNil]); - storePointer: ObjStackTopx ofObjStack: freeOrNewPage withValue: 0; - storePointer: objStackRootIndex ofObject: hiddenRootsObj withValue: freeOrNewPage. - self assert: (self isValidObjStackAt: objStackRootIndex). - "Added a new page; now update and answer the relevant cached first page." - stackOrNil := self updateRootOfObjStackAt: objStackRootIndex with: freeOrNewPage]. + stackOrNil := self + fetchPointer: objStackRootIndex + ofObject: hiddenRootsObj. + (stackOrNil = nilObj or: [ + (self fetchPointer: ObjStackTopx ofObject: stackOrNil) + >= ObjStackLimit ]) ifTrue: [ + freeOrNewPage := stackOrNil = nilObj + ifTrue: [ 0 ] + ifFalse: [ + self + fetchPointer: ObjStackFreex + ofObject: stackOrNil ]. + freeOrNewPage ~= 0 + ifTrue: [ "the free page list is always on the new page." + self + storePointer: ObjStackFreex + ofObjStack: stackOrNil + withValue: 0. + self assert: (marking not or: [ self isMarked: freeOrNewPage ]) ] + ifFalse: [ + freeOrNewPage := self + allocateSlotsInOldSpace: ObjStackPageSlots + format: self wordIndexableFormat + classIndex: self wordSizeClassIndexPun. + freeOrNewPage ifNil: [ "Allocate a new segment an retry. This is very uncommon. But it happened to me (Clement)." + self + growOldSpaceByAtLeast: ObjStackPageSlots + callingOperation: 'ensuring room on ObjStack'. + freeOrNewPage := self + allocateSlotsInOldSpace: ObjStackPageSlots + format: self wordIndexableFormat + classIndex: self wordSizeClassIndexPun. + freeOrNewPage ifNil: [ + self error: 'no memory to allocate or extend obj stack' ] ]. + self + storePointer: ObjStackFreex + ofObjStack: freeOrNewPage + withValue: 0. + marking ifTrue: [ self setIsMarkedOf: freeOrNewPage to: true ] ]. + self + storePointer: ObjStackMyx + ofObjStack: freeOrNewPage + withValue: objStackRootIndex; + storePointer: ObjStackNextx + ofObjStack: freeOrNewPage + withValue: (stackOrNil = nilObj + ifTrue: [ 0 ] + ifFalse: [ stackOrNil ]); + storePointer: ObjStackTopx ofObjStack: freeOrNewPage withValue: 0; + storePointer: objStackRootIndex + ofObject: hiddenRootsObj + withValue: freeOrNewPage. + self assert: (self isValidObjStackAt: objStackRootIndex). + "Added a new page; now update and answer the relevant cached first page." + stackOrNil := self + updateRootOfObjStackAt: objStackRootIndex + with: freeOrNewPage ]. self assert: (self isValidObjStackAt: objStackRootIndex). - ^stackOrNil + ^ stackOrNil ] { #category : 'interpreter access' } @@ -6041,11 +6069,14 @@ SpurMemoryManager >> growHeadroom: aValue [ ] { #category : 'growing/shrinking memory' } -SpurMemoryManager >> growOldSpaceByAtLeast: minAmmount [ +SpurMemoryManager >> growOldSpaceByAtLeast: minAmmount callingOperation: aString [ "Attempt to grow memory by at least minAmmount bytes. Answer the size of the new segment in bytes, or nil if the attempt failed." | ammount headroom total start interval | + + + "statGrowMemory counts attempts, not successes." statGrowMemory := statGrowMemory + 1."we need to include overhead for a new object header plus the segment bridge." ammount := minAmmount + (self baseHeaderSize * 2 + self bridgeSize). @@ -6058,13 +6089,13 @@ SpurMemoryManager >> growOldSpaceByAtLeast: minAmmount [ maxOldSpaceSize > 0 ifTrue: [total := segmentManager totalBytesInSegments. total >= maxOldSpaceSize ifTrue:[ - self logError: 'Could not allocate more memory. MaxOldSpaceSize reached.'. + self logError: 'Could not allocate more memory while %s. MaxOldSpaceSize reached.' _: aString. ^nil]. headroom := maxOldSpaceSize - total. headroom < ammount ifTrue: [headroom < (minAmmount + (self baseHeaderSize * 2 + self bridgeSize)) ifTrue: [ - self logError: 'Required space is bigger than the headroom. Could not allocate'. + self logError: 'Required space is bigger than the headroom while %s. Could not allocate' _: aString. ^nil]. ammount := headroom]]. @@ -6090,9 +6121,12 @@ SpurMemoryManager >> growToAccomodateContainerWithNumSlots: numSlots [ "Grow memory to accomodate a container (an Array) with numSlots. Grow by at least the growHeadroom. Supports allInstancesOf: and allObjects. Answer the size of the new segment in bytes, or nil if the attempt failed." + | delta | delta := self baseHeaderSize * 2 + (numSlots * self bytesPerOop). - ^ self growOldSpaceByAtLeast: (growHeadroom max: delta) + ^ self + growOldSpaceByAtLeast: (growHeadroom max: delta) + callingOperation: 'growing to accomodate allObjects / allInstances container' ] { #category : 'header access' } @@ -8442,13 +8476,15 @@ SpurMemoryManager >> lookupAddress: address [ { #category : 'free space' } SpurMemoryManager >> lowSpaceThreshold: threshold [ + lowSpaceThreshold := threshold. "N.B. The threshold > 0 guard eliminates a warning when self lowSpaceThreshold: 0 is inlined into setSignalLowSpaceFlagAndSaveProcess" - (threshold > 0 - and: [totalFreeOldSpace < threshold]) ifTrue: - [self growOldSpaceByAtLeast: threshold - totalFreeOldSpace]. + (threshold > 0 and: [ totalFreeOldSpace < threshold ]) ifTrue: [ + self + growOldSpaceByAtLeast: threshold - totalFreeOldSpace + callingOperation: 'ensuring enough space after setting lowSpaceThreshold' ]. self assert: totalFreeOldSpace >= lowSpaceThreshold ] @@ -12605,17 +12641,20 @@ SpurMemoryManager >> sufficientSpaceAfterGC: numBytes [ | heapSizePostGC | self assert: numBytes = 0. self scavengingGCTenuringIf: TenureByAge. - heapSizePostGC := segmentManager totalOldSpaceCapacity - totalFreeOldSpace. - (heapSizePostGC - heapSizeAtPreviousGC) asFloat / heapSizeAtPreviousGC >= heapGrowthToSizeGCRatio ifTrue: - [self fullGC]. - [totalFreeOldSpace < growHeadroom - and: [(self growOldSpaceByAtLeast: 0) notNil]] whileTrue: - [totalFreeOldSpace >= growHeadroom ifTrue: - [^true]]. - lowSpaceThreshold > totalFreeOldSpace ifTrue: "space is low" - [lowSpaceThreshold := 0. "avoid signalling low space twice" - ^false]. - ^true + heapSizePostGC := segmentManager totalOldSpaceCapacity + - totalFreeOldSpace. + (heapSizePostGC - heapSizeAtPreviousGC) asFloat + / heapSizeAtPreviousGC >= heapGrowthToSizeGCRatio ifTrue: [ + self fullGC ]. + [ + totalFreeOldSpace < growHeadroom and: [ + (self growOldSpaceByAtLeast: 0 callingOperation: 'ensuring sufficient space after GC') + notNil ] ] whileTrue: [ + totalFreeOldSpace >= growHeadroom ifTrue: [ ^ true ] ]. + lowSpaceThreshold > totalFreeOldSpace ifTrue: [ "space is low" + lowSpaceThreshold := 0. "avoid signalling low space twice" + ^ false ]. + ^ true ] { #category : 'allocation' } diff --git a/smalltalksrc/VMMaker/SpurSelectiveCompactor.class.st b/smalltalksrc/VMMaker/SpurSelectiveCompactor.class.st index 45e3adaf89..2b0331a86b 100644 --- a/smalltalksrc/VMMaker/SpurSelectiveCompactor.class.st +++ b/smalltalksrc/VMMaker/SpurSelectiveCompactor.class.st @@ -206,18 +206,22 @@ SpurSelectiveCompactor >> findNextSegmentToCompact [ SpurSelectiveCompactor >> findOrAllocateSegmentToFill [ "There was no compacted segments from past GC that we can directly re-use. We need either to find an empty segment or allocate a new one." + | segIndex | self findAndSetSegmentToFill. - segmentToFill ifNotNil: [^0]. + segmentToFill ifNotNil: [ ^ 0 ]. "No empty segment. We need to allocate a new one" - (manager growOldSpaceByAtLeast: manager growHeadroom) ifNil: ["failed to allocate"^0]. + (manager + growOldSpaceByAtLeast: manager growHeadroom + callingOperation: 'finding or allocating segment to fill') ifNil: [ "failed to allocate" + ^ 0 ]. "We don't know which segment it is that we've just allocated... So we look for it... This is a bit dumb." segIndex := self findAndSetSegmentToFill. "Lilliputian performance hack management... Last lilliputian of new segment is same as prev because no lilliputian in new segment" - self setLastLilliputianChunkAtindex: segIndex to: (self lastLilliputianChunkAtIndex: segIndex - 1). - self assert: segmentToFill ~~ nil. - - + self + setLastLilliputianChunkAtindex: segIndex + to: (self lastLilliputianChunkAtIndex: segIndex - 1). + self assert: segmentToFill ~~ nil ] { #category : 'segment access' } diff --git a/smalltalksrc/VMMaker/TVMSpurMemoryManagerSimulator.trait.st b/smalltalksrc/VMMaker/TVMSpurMemoryManagerSimulator.trait.st index 3a0233ca7c..fa11ab43a0 100644 --- a/smalltalksrc/VMMaker/TVMSpurMemoryManagerSimulator.trait.st +++ b/smalltalksrc/VMMaker/TVMSpurMemoryManagerSimulator.trait.st @@ -38,6 +38,18 @@ TVMSpurMemoryManagerSimulator >> firstIndexableField: objOop [ ifFalse: ["byte objects (including CompiledMethod" #'char *']) ] +{ #category : 'growing/shrinking memory' } +TVMSpurMemoryManagerSimulator >> growOldSpaceByAtLeast: minAmmount callingOperation: aString [ + "Attempt to grow memory by at least minAmmount. + Answer the size of the new segment, or nil if the attempt failed. + Override to not grow during the Spur image bootstrap." + + ^ self bootstrapping ifFalse: [ + super + growOldSpaceByAtLeast: minAmmount + callingOperation: aString ] +] + { #category : 'initialization' } TVMSpurMemoryManagerSimulator >> initializeFreeSpaceForFacadeFrom: base to: limit [ "c.f. initializeFreeSpacePostLoad: freeListObj." diff --git a/smalltalksrc/VMMaker/VMRememberedSet.class.st b/smalltalksrc/VMMaker/VMRememberedSet.class.st index eefd81ae9c..4ae3fa980a 100644 --- a/smalltalksrc/VMMaker/VMRememberedSet.class.st +++ b/smalltalksrc/VMMaker/VMRememberedSet.class.st @@ -133,37 +133,37 @@ VMRememberedSet >> fudge: anObject [ { #category : 'remembered set' } VMRememberedSet >> growRememberedSet [ - | obj numSlots newObj base | - "Don't ruin locality in remember:" + - - - obj := self objectOop. + | obj numSlots newObj base | + obj := self objectOop. "Don't ruin locality in remember:" numSlots := manager numSlotsOf: obj. self assert: numSlots >= 1024. newObj := manager allocatePinnedSlots: numSlots * 2. - newObj ifNil: - [newObj := manager allocatePinnedSlots: numSlots + 1024. - newObj ifNil: - [(manager growOldSpaceByAtLeast: (numSlots + 1024) * manager wordSize) ifNil: [self error: 'could not grow remembered set']. - newObj := manager allocatePinnedSlots: numSlots + 1024. "cannot fail"]]. + newObj ifNil: [ + newObj := manager allocatePinnedSlots: numSlots + 1024. + newObj ifNil: [ + (manager + growOldSpaceByAtLeast: numSlots + 1024 * manager wordSize + callingOperation: 'growing remembered set') ifNil: [ + self error: 'could not grow remembered set' ]. + newObj := manager allocatePinnedSlots: numSlots + 1024 "cannot fail" ] ]. manager rememberedSet: rootIndex oop: newObj. base := self rememberedSetArrayPointerFromObject: newObj. - 0 to: rememberedSetSize - 1 do: - [:i| base at: i put: (self rememberedSetArray at: i)]. + 0 to: rememberedSetSize - 1 do: [ :i | + base at: i put: (self rememberedSetArray at: i) ]. "if growing in the middle of a GC, need to preserve marked status." - (manager isMarked: obj) ifTrue: - [manager + (manager isMarked: obj) ifTrue: [ + manager setIsMarkedOf: newObj to: true; - setIsMarkedOf: obj to: false]. + setIsMarkedOf: obj to: false ]. manager freeObject: obj. self rememberedSetArray: base. self rememberedSetLimit: (manager numSlotsOf: newObj). self setRememberedSetRedZone - ] { #category : 'initialization' } @@ -524,40 +524,40 @@ VMRememberedSet >> setRememberedSetSize: aNewValue [ { #category : 'remembered set' } VMRememberedSet >> shrinkRememberedSet [ - | obj numSlots newObj base | - + | obj numSlots newObj base | rememberedSetLimit >= 2048 ifFalse: [ ^ self ]. - rememberedSetSize < (self rememberedSetLimit // 2) - ifFalse: [ ^ self ]. + rememberedSetSize < (self rememberedSetLimit // 2) ifFalse: [ ^ self ]. obj := self objectOop. - numSlots := ((manager numSlotsOf: obj) // 2) max: 2048. + numSlots := (manager numSlotsOf: obj) // 2 max: 2048. newObj := manager allocatePinnedSlots: numSlots. - newObj ifNil: - [newObj := manager allocatePinnedSlots: numSlots. - newObj ifNil: - [(manager growOldSpaceByAtLeast: numSlots * manager wordSize) ifNil: [self error: 'could not shrink remembered set']. - newObj := manager allocatePinnedSlots: numSlots. "cannot fail"]]. + newObj ifNil: [ + newObj := manager allocatePinnedSlots: numSlots. + newObj ifNil: [ + (manager + growOldSpaceByAtLeast: numSlots * manager wordSize + callingOperation: 'shrinking RememberedSet') ifNil: [ + self error: 'could not shrink remembered set' ]. + newObj := manager allocatePinnedSlots: numSlots "cannot fail" ] ]. manager rememberedSet: rootIndex oop: newObj. base := self rememberedSetArrayPointerFromObject: newObj. - 0 to: rememberedSetSize - 1 do: - [:i| base at: i put: (self rememberedSetArray at: i)]. + 0 to: rememberedSetSize - 1 do: [ :i | + base at: i put: (self rememberedSetArray at: i) ]. "if growing in the middle of a GC, need to preserve marked status." - (manager isMarked: obj) ifTrue: - [manager + (manager isMarked: obj) ifTrue: [ + manager setIsMarkedOf: newObj to: true; - setIsMarkedOf: obj to: false]. + setIsMarkedOf: obj to: false ]. manager freeObject: obj. self rememberedSetArray: base. self rememberedSetLimit: (manager numSlotsOf: newObj). self setRememberedSetRedZone - ] { #category : 'scavenger' } diff --git a/smalltalksrc/VMMakerTests/VMImageReadingTest.class.st b/smalltalksrc/VMMakerTests/VMImageReadingTest.class.st index 85dc4c29f5..90e609d175 100644 --- a/smalltalksrc/VMMakerTests/VMImageReadingTest.class.st +++ b/smalltalksrc/VMMakerTests/VMImageReadingTest.class.st @@ -163,34 +163,43 @@ VMImageReadingTest >> testSavedImageSavesObjectFromPermanentSpace [ { #category : 'tests' } VMImageReadingTest >> testSavingImageWithThreeSegmentsIsCorrectlySqueezed [ - + | firstNewSegmentSize secondNewSegmentSize obj newObj originalObjHash | - firstNewSegmentSize := memory growOldSpaceByAtLeast: 12 * 1024. - secondNewSegmentSize := memory growOldSpaceByAtLeast: 12 * 1024. - + firstNewSegmentSize := memory + growOldSpaceByAtLeast: 12 * 1024 + callingOperation: 'testSavingImageWithThreeSegmentsIsCorrectlySqueezed'. + secondNewSegmentSize := memory + growOldSpaceByAtLeast: 12 * 1024 + callingOperation: 'testSavingImageWithThreeSegmentsIsCorrectlySqueezed'. + self assert: memory segmentManager numSegments equals: 3. - self assert: (memory segmentManager segments at: 1) segSize equals: firstNewSegmentSize. - self assert: (memory segmentManager segments at: 2) segSize equals: secondNewSegmentSize. + self + assert: (memory segmentManager segments at: 1) segSize + equals: firstNewSegmentSize. + self + assert: (memory segmentManager segments at: 2) segSize + equals: secondNewSegmentSize. - obj := self newOldSpaceObjectWithSlots: (firstNewSegmentSize / 2) // memory wordSize. - memory splObj: 4 put: obj. "Store object in SpecialObjects Array to keep it" + obj := self newOldSpaceObjectWithSlots: + firstNewSegmentSize / 2 // memory wordSize. + memory splObj: 4 put: obj. "Store object in SpecialObjects Array to keep it" originalObjHash := memory hashBitsOf: obj. - "Ensure the object is created in the last segment". + "Ensure the object is created in the last segment" self assert: obj > (memory segmentManager segments at: 2) segStart. self assert: obj < (memory segmentManager segments at: 2) segLimit. - self saveImage. + self saveImage. self loadImage. self assert: memory segmentManager numSegments equals: 1. - - newObj := memory splObj: 4. - - self assert: originalObjHash equals: (memory hashBitsOf: newObj). + + newObj := memory splObj: 4. + + self assert: originalObjHash equals: (memory hashBitsOf: newObj) ] { #category : 'tests' } diff --git a/smalltalksrc/VMMakerTests/VMSegmentsImageFormatTest.class.st b/smalltalksrc/VMMakerTests/VMSegmentsImageFormatTest.class.st index b5bfa609cb..df3bbbdc82 100644 --- a/smalltalksrc/VMMakerTests/VMSegmentsImageFormatTest.class.st +++ b/smalltalksrc/VMMakerTests/VMSegmentsImageFormatTest.class.st @@ -10,12 +10,15 @@ Class { VMSegmentsImageFormatTest >> testImageWithTwoSegmentsHasCorrectSizeForFirstSegment [ | header newSegmentSize | - - newSegmentSize := memory growOldSpaceByAtLeast: 10000. - + newSegmentSize := memory + growOldSpaceByAtLeast: 10000 + callingOperation: 'testImageWithTwoSegmentsHasCorrectSizeForFirstSegment'. + self assert: memory segmentManager numSegments equals: 2. - self assert: (memory segmentManager segments at: 1) segSize equals: newSegmentSize. - + self + assert: (memory segmentManager segments at: 1) segSize + equals: newSegmentSize. + self saveImage. header := self readHeader. @@ -29,12 +32,15 @@ VMSegmentsImageFormatTest >> testImageWithTwoSegmentsHasCorrectSizeForFirstSegme VMSegmentsImageFormatTest >> testImageWithTwoSegmentsHasCorrectSizeForFullImage [ | header newSegmentSize | - - newSegmentSize := memory growOldSpaceByAtLeast: 10000. - + newSegmentSize := memory + growOldSpaceByAtLeast: 10000 + callingOperation: 'testImageWithTwoSegmentsHasCorrectSizeForFullImage'. + self assert: memory segmentManager numSegments equals: 2. - self assert: (memory segmentManager segments at: 1) segSize equals: newSegmentSize. - + self + assert: (memory segmentManager segments at: 1) segSize + equals: newSegmentSize. + self saveImage. header := self readHeader. @@ -42,7 +48,7 @@ VMSegmentsImageFormatTest >> testImageWithTwoSegmentsHasCorrectSizeForFullImage self assert: header firstSegSize equals: (memory segmentManager segments at: 0) segSize. - + self assert: header dataSize equals: header firstSegSize + newSegmentSize @@ -52,12 +58,15 @@ VMSegmentsImageFormatTest >> testImageWithTwoSegmentsHasCorrectSizeForFullImage VMSegmentsImageFormatTest >> testImageWithTwoSegmentsRespectGrowHeadroomWhenIsBigger [ | newSegmentSize | - - newSegmentSize := memory growOldSpaceByAtLeast: 10000. - + newSegmentSize := memory + growOldSpaceByAtLeast: 10000 + callingOperation: 'testImageWithTwoSegmentsRespectGrowHeadroomWhenIsBigger'. + self assert: memory segmentManager numSegments equals: 2. - self assert: (memory segmentManager segments at: 1) segSize equals: newSegmentSize. - + self + assert: (memory segmentManager segments at: 1) segSize + equals: newSegmentSize. + self assert: newSegmentSize >= memory growHeadroom ] @@ -65,14 +74,17 @@ VMSegmentsImageFormatTest >> testImageWithTwoSegmentsRespectGrowHeadroomWhenIsBi VMSegmentsImageFormatTest >> testImageWithTwoSegmentsRespectGrowHeadroomWhenIsSmaller [ | newSegmentSize | - memory growHeadroom: 4096. - - newSegmentSize := memory growOldSpaceByAtLeast: 10000. - + + newSegmentSize := memory + growOldSpaceByAtLeast: 10000 + callingOperation: 'testImageWithTwoSegmentsRespectGrowHeadroomWhenIsSmaller'. + self assert: memory segmentManager numSegments equals: 2. - self assert: (memory segmentManager segments at: 1) segSize equals: newSegmentSize. - + self + assert: (memory segmentManager segments at: 1) segSize + equals: newSegmentSize. + self assert: newSegmentSize >= memory growHeadroom ] diff --git a/smalltalksrc/VMMakerTests/VMSpurOldSpaceGarbageCollectorTest.class.st b/smalltalksrc/VMMakerTests/VMSpurOldSpaceGarbageCollectorTest.class.st index 2000f8d486..8f1c053ed1 100644 --- a/smalltalksrc/VMMakerTests/VMSpurOldSpaceGarbageCollectorTest.class.st +++ b/smalltalksrc/VMMakerTests/VMSpurOldSpaceGarbageCollectorTest.class.st @@ -633,21 +633,25 @@ VMSpurOldSpaceGarbageCollectorTest >> testEphemeronWithImmediateKeyShouldNotFail VMSpurOldSpaceGarbageCollectorTest >> testGrowOldSpace [ | freespace freespace2 slotsNumber anObjectOop | - memory fullGC. - + freespace := memory totalFreeOldSpace. - slotsNumber := freespace / memory wordSize. - memory growOldSpaceByAtLeast: slotsNumber. + slotsNumber := freespace / memory wordSize. + memory + growOldSpaceByAtLeast: slotsNumber + callingOperation: 'testGrowOldSpace'. + freespace2 := memory totalFreeOldSpace. - self assert: freespace*2 <= freespace2. - + self assert: freespace * 2 <= freespace2. + anObjectOop := self newObjectWithSlots: slotsNumber. self deny: anObjectOop equals: nil. - self assert: freespace2 - (memory bytesInObject: anObjectOop) equals: memory totalFreeOldSpace. - + self + assert: freespace2 - (memory bytesInObject: anObjectOop) + equals: memory totalFreeOldSpace. + memory fullGC. - self assert: freespace equals: memory totalFreeOldSpace. + self assert: freespace equals: memory totalFreeOldSpace ] { #category : 'ephemerons' }