ObjectMemory>>contiguousFixedSpaceAt: seems to report non-contiguous memory

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

ObjectMemory>>contiguousFixedSpaceAt: seems to report non-contiguous memory

David Lattimore
I've been getting occasional crashes when trying to allocate ByteArrays
using newInFixedSpace:.  The system has plenty of free memory, so the VM
can certainly allocate more.  Some investigation has revealed that the
following seems to be happening:

- The primitive in newInFixedSpace: fails
- The failure handling code decides to favour garbage collection over
more allocation and runs a garbage collect.
- The garbage collect frees some memory.
- MemoryPolicy>>makeSpaceFor:ofType: thinks it has freed enough
according to the numbers it gets back from contiguousFixedSpaceAt:, but
in actual fact the memory is fragmented and contiguousFixedSpaceAt: is
not taking this into account.
- newInFixedSpaceNoRetry: has a second attempt but also fails because of
the fragmentation.

Had the failure handling code known the actual contiguous fixed space it
would have known that it was not enough and increased the size of fixed
space.

The following is the output from my test script to reproduce this:

--------------------
Fixed space contiguous sizes: #(204780)
total fixed space size: 204800
Maximum that can be allocated without growing fixed space: 204754

After allocating 150 x 1000 fixed space ByteArrays: #(50580)
total fixed space size: 204800
Maximum that can be allocated without growing fixed space: 50554

After freeing every second allocation: #(126180)
total fixed space size: 204800
Maximum that can be allocated without growing fixed space: 50554
--------------------

Freeing every second allocation should not have increased contiguous
fixed space by more than one or two thousand that may have happened to
be adjacent to the 50k block of free fixed space.  Also, note that the
maximum that could then be allocated (50554) was less than what should
have been the unfragmented size (50580).  This may be due to more
fragmentation not caused my me (although this is a clean image), or due
to some other fixed size not being taken into account.

Following is the script:

--------------------
fragmentedAllocations := nil.
log := String new writeStream.

reportFixedMemoryInfo := [:message |
ObjectMemory globalGarbageCollect.
log cr;
    cr; nextPutAll: message; print: ((1 to: ObjectMemory current
fixedSegments) collect: [:x | ObjectMemory current
contiguousFixedSpaceAt: x]);
    cr; nextPutAll: 'total fixed space size: '; print: ObjectMemory
current fixedBytes.
actuallyAllocated := ObjectMemory current contiguousFixedSpaceAt: 1.
[ByteArray newInFixedSpaceNoRetry: actuallyAllocated. ObjectMemory
globalGarbageCollect.]
    on: ObjectMemory allocationFailedSignal
    do: [:ex |
        actuallyAllocated := actuallyAllocated - 1.
        ex retry].
log cr; nextPutAll: 'Maximum that can be allocated without growing fixed
space: '; print: actuallyAllocated].

reportFixedMemoryInfo value: 'Fixed space contiguous sizes: '.

fragmentedAllocations := OrderedCollection new.
150 timesRepeat: [
    fragmentedAllocations add: (ByteArray newInFixedSpace: 1000)].

reportFixedMemoryInfo value: 'After allocating 150 x 1000 fixed space
ByteArrays: '.

1 to: fragmentedAllocations size by: 2 do: [:x |
    fragmentedAllocations at: x put: nil].

ObjectMemory globalGarbageCollect.
reportFixedMemoryInfo value: 'After freeing every second allocation: '.

log contents
--------------------

David

Reply | Threaded
Open this post in threaded view
|

Re: ObjectMemory>>contiguousFixedSpaceAt: seems to report non-contiguous memory

Travis Griggs-3
On Nov 20, 2006, at 21:00, David Price wrote:

I've been getting occasional crashes when trying to allocate ByteArrays using newInFixedSpace:.  The system has plenty of free memory, so the VM can certainly allocate more.  Some investigation has revealed that the following seems to be happening:


- The primitive in newInFixedSpace: fails

- The failure handling code decides to favour garbage collection over more allocation and runs a garbage collect.

- The garbage collect frees some memory.

- MemoryPolicy>>makeSpaceFor:ofType: thinks it has freed enough according to the numbers it gets back from contiguousFixedSpaceAt:, but in actual fact the memory is fragmented and contiguousFixedSpaceAt: is not taking this into account.

- newInFixedSpaceNoRetry: has a second attempt but also fails because of the fragmentation.


Had the failure handling code known the actual contiguous fixed space it would have known that it was not enough and increased the size of fixed space.


The following is the output from my test script to reproduce this:


--------------------

Fixed space contiguous sizes: #(204780)

total fixed space size: 204800

Maximum that can be allocated without growing fixed space: 204754


After allocating 150 x 1000 fixed space ByteArrays: #(50580)

total fixed space size: 204800

Maximum that can be allocated without growing fixed space: 50554


After freeing every second allocation: #(126180)

total fixed space size: 204800

Maximum that can be allocated without growing fixed space: 50554

--------------------


Freeing every second allocation should not have increased contiguous fixed space by more than one or two thousand that may have happened to be adjacent to the 50k block of free fixed space.  Also, note that the maximum that could then be allocated (50554) was less than what should have been the unfragmented size (50580).  This may be due to more fragmentation not caused my me (although this is a clean image), or due to some other fixed size not being taken into account.


Yay! I'm glad someone else is hitting this and making some headway. I posted about issues with this on Feb 16 and Feb 21. And never got anywhere. I'd like this to work and work well.

I had a hunch I tried with your script actually. And it worked. Change your "free every 2nd" to free as 2 to: count by: 2. And then you will get the result you are looking for (or close, it's 51582). Because you went from 1 to: count by: 2, you got rid of the N - 1 slot, but not the Nth one. So no extra contiguous room became available.

I'd love for someone to explain the results I was seeing with the Feb 16/21 posts. :)

--
Travis Griggs
Objologist
"You A students, you'll be back soon teaching here with me. You B students, you'll actually go on to be real engineers. You C students, you'll go into management and tell the A and B students what to do." - My Fluid Dynamics Professor whom I have yet to disprove


Reply | Threaded
Open this post in threaded view
|

Re: ObjectMemory>>contiguousFixedSpaceAt: seems to report non-contiguous memory

eliot-2
In reply to this post by David Lattimore
Hi David,

        you're exactly right.  I just looked at ObjectMemory>>contiguousFixedSpaceAt: and iots clear that waht it answers is the total amount of free space in a fixed-space segment, which has nothing at all to do with the maximum contiguous free space in the segment.  I'll create an AR.  Thanks!

Foer reference here's the erroneous definition, which is correcty only for empty free-space segments:

contiguousFixedSpaceAt: index
        "Answer the number of bytes of contiguous free space in the specified FixedSpace
        segment."
        "ObjectMemory current contiguousFixedSpaceAt: 1"

        ^(self fixedSegmentSizeAt: index)
                - (self ftEntriesToBytes: (self fixedTableSizeAt: index))
                - (self fixedDataSizeAt: index)

David Price <[hidden email]> wrote:

| I've been getting occasional crashes when trying to allocate ByteArrays
| using newInFixedSpace:.  The system has plenty of free memory, so the VM
| can certainly allocate more.  Some investigation has revealed that the
| following seems to be happening:

| - The primitive in newInFixedSpace: fails
| - The failure handling code decides to favour garbage collection over
| more allocation and runs a garbage collect.
| - The garbage collect frees some memory.
| - MemoryPolicy>>makeSpaceFor:ofType: thinks it has freed enough
| according to the numbers it gets back from contiguousFixedSpaceAt:, but
| in actual fact the memory is fragmented and contiguousFixedSpaceAt: is
| not taking this into account.
| - newInFixedSpaceNoRetry: has a second attempt but also fails because of
| the fragmentation.

| Had the failure handling code known the actual contiguous fixed space it
| would have known that it was not enough and increased the size of fixed
| space.

| The following is the output from my test script to reproduce this:

| --------------------
| Fixed space contiguous sizes: #(204780)
| total fixed space size: 204800
| Maximum that can be allocated without growing fixed space: 204754

| After allocating 150 x 1000 fixed space ByteArrays: #(50580)
| total fixed space size: 204800
| Maximum that can be allocated without growing fixed space: 50554

| After freeing every second allocation: #(126180)
| total fixed space size: 204800
| Maximum that can be allocated without growing fixed space: 50554
| --------------------

| Freeing every second allocation should not have increased contiguous
| fixed space by more than one or two thousand that may have happened to
| be adjacent to the 50k block of free fixed space.  Also, note that the
| maximum that could then be allocated (50554) was less than what should
| have been the unfragmented size (50580).  This may be due to more
| fragmentation not caused my me (although this is a clean image), or due
| to some other fixed size not being taken into account.

| Following is the script:

| --------------------
| fragmentedAllocations := nil.
| log := String new writeStream.

| reportFixedMemoryInfo := [:message |
| ObjectMemory globalGarbageCollect.
| log cr;
|     cr; nextPutAll: message; print: ((1 to: ObjectMemory current
| fixedSegments) collect: [:x | ObjectMemory current
| contiguousFixedSpaceAt: x]);
|     cr; nextPutAll: 'total fixed space size: '; print: ObjectMemory
| current fixedBytes.
| actuallyAllocated := ObjectMemory current contiguousFixedSpaceAt: 1.
| [ByteArray newInFixedSpaceNoRetry: actuallyAllocated. ObjectMemory
| globalGarbageCollect.]
|     on: ObjectMemory allocationFailedSignal
|     do: [:ex |
|         actuallyAllocated := actuallyAllocated - 1.
|         ex retry].
| log cr; nextPutAll: 'Maximum that can be allocated without growing fixed
| space: '; print: actuallyAllocated].

| reportFixedMemoryInfo value: 'Fixed space contiguous sizes: '.

| fragmentedAllocations := OrderedCollection new.
| 150 timesRepeat: [
|     fragmentedAllocations add: (ByteArray newInFixedSpace: 1000)].

| reportFixedMemoryInfo value: 'After allocating 150 x 1000 fixed space
| ByteArrays: '.

| 1 to: fragmentedAllocations size by: 2 do: [:x |
|     fragmentedAllocations at: x put: nil].

| ObjectMemory globalGarbageCollect.
| reportFixedMemoryInfo value: 'After freeing every second allocation: '.

| log contents
| --------------------

| David
---
Eliot Miranda                 ,,,^..^,,,                mailto:[hidden email]
VisualWorks Engineering, Cincom  Smalltalk: scene not herd  Tel +1 408 216 4581
3350 Scott Blvd, Bldg 36 Suite B, Santa Clara, CA 95054 USA Fax +1 408 216 4500


Reply | Threaded
Open this post in threaded view
|

Re: ObjectMemory>>contiguousFixedSpaceAt: seems to report non-contiguous memory

David Lattimore
In reply to this post by Travis Griggs-3
Travis Griggs wrote:
> Yay! I'm glad someone else is hitting this and making some headway. I
> posted about issues with this on Feb 16 and Feb 21. And never got
> anywhere. I'd like this to work and work well.
>
I've worked around the problem for now by doing up to three attempts at
allocation, where prior to the third attempt it will always expand free
space, so you can be sure to get enough non-fragmented memory.  This is
kind of ugly, but I think it should always work.  It's in public store
as "FixedSpaceWorkaround".
> I had a hunch I tried with your script actually. And it worked. Change
> your "free every 2nd" to free as 2 to: count by: 2. And then you will
> get the result you are looking for (or close, it's 51582). Because you
> went from 1 to: count by: 2, you got rid of the N - 1 slot, but not
> the Nth one. So no extra contiguous room became available.
>
Ah, yes, of course :)
> I'd love for someone to explain the results I was seeing with the Feb
> 16/21 posts. :)
You mean where it reports 1 fixed space object?  That's certainly odd.

David

Reply | Threaded
Open this post in threaded view
|

Re: ObjectMemory>>contiguousFixedSpaceAt: seems to report non-contiguous memory

David Lattimore
In reply to this post by eliot-2
[hidden email] wrote:

> Hi David,
>
> you're exactly right.  I just looked at ObjectMemory>>contiguousFixedSpaceAt: and iots clear that waht it answers is the total amount of free space in a fixed-space segment, which has nothing at all to do with the maximum contiguous free space in the segment.  I'll create an AR.  Thanks!
>
> Foer reference here's the erroneous definition, which is correcty only for empty free-space segments:
>
> contiguousFixedSpaceAt: index
> "Answer the number of bytes of contiguous free space in the specified FixedSpace
> segment."
> "ObjectMemory current contiguousFixedSpaceAt: 1"
>
> ^(self fixedSegmentSizeAt: index)
> - (self ftEntriesToBytes: (self fixedTableSizeAt: index))
> - (self fixedDataSizeAt: index)
>
>  
Hi Eliot,

ObjectMemory>>contiguousOldSpaceAt: is implemented similarly.  This I
guess will be correct if old space is never fragmented, which I guess
seems kind of plausible, although if you did a non-compacting GC on old
space then it seems less likely.

Thanks,
David

Reply | Threaded
Open this post in threaded view
|

Re: ObjectMemory>>contiguousFixedSpaceAt: seems to report non-contiguous memory

eliot-2
In reply to this post by David Lattimore
David Price <[hidden email]> wrote:

| [hidden email] wrote:
| > Hi David,
| >
| > you're exactly right.  I just looked at ObjectMemory>>contiguousFixedSpaceAt: and iots clear that waht it answers is the total amount of free space in a fixed-space segment, which has nothing at all to do with the maximum contiguous free space in the segment.  I'll create an AR.  Thanks!
| >
| > Foer reference here's the erroneous definition, which is correcty only for empty free-space segments:
| >
| > contiguousFixedSpaceAt: index
| > "Answer the number of bytes of contiguous free space in the specified FixedSpace
| > segment."
| > "ObjectMemory current contiguousFixedSpaceAt: 1"
| >
| > ^(self fixedSegmentSizeAt: index)
| > - (self ftEntriesToBytes: (self fixedTableSizeAt: index))
| > - (self fixedDataSizeAt: index)
| >
| >
| Hi Eliot,

| ObjectMemory>>contiguousOldSpaceAt: is implemented similarly.  This I
| guess will be correct if old space is never fragmented, which I guess
| seems kind of plausible, although if you did a non-compacting GC on old
| space then it seems less likely.

The similar implementation is because contiguousOldSpaceAt: was (incorrectly) copy-pasted to implement contiguousFixedSpaceAt:.  The information for each oldSpace segment is the size of the segment, the length of the object table and the size of the objects.  The OT asnd objects start at opposite ends and grow together.  So the hole in the middle is contiguous free space.  There may be free blocks in the object portion of the segment, and these may be larger than the hole, but the hole is still contiguous free space.

A fixed-space segment isn't organized like this at all.  Its a list of blocks, some free, some used.  The used blocks can never be moved (by definition; fixed space segments get compacted on image load).  So there's no hole in the middle to compute.  One would have to search the list to find the largest free block.
---
Eliot Miranda                 ,,,^..^,,,                mailto:[hidden email]
VisualWorks Engineering, Cincom  Smalltalk: scene not herd  Tel +1 408 216 4581
3350 Scott Blvd, Bldg 36 Suite B, Santa Clara, CA 95054 USA Fax +1 408 216 4500