Teach sb_free about large objects; avoid hysteresis therein.
authorRobert Haas <rhaas@postgresql.org>
Mon, 5 May 2014 14:34:02 +0000 (14:34 +0000)
committerRobert Haas <rhaas@postgresql.org>
Mon, 5 May 2014 14:34:02 +0000 (14:34 +0000)
src/backend/utils/mmgr/sb_alloc.c

index b306fc37319d70f7827fa1a415ee546cc800692c..528ee2da92705d44c1c75e1609c86d7e16e1e6a4 100644 (file)
@@ -306,12 +306,27 @@ sb_free(void *ptr)
                        LWLockAcquire(lock, LW_EXCLUSIVE);
        }
 
-       /* XXX. What about large objects? */
-
        /* Compute the object size. */
        size_class = span->size_class;
        obsize = sb_size_classes[size_class];
 
+       /* If it's a large object, free the entire span. */
+       if (size_class == SB_SCLASS_SPAN_LARGE)
+       {
+               sb_heap *heap = relptr_access(base, span->parent);
+               Size    first_page;
+
+               sb_unlink_span(base, heap, span);
+               first_page = fpm_pointer_to_page(fpm_base,
+                                                                                relptr_access(base, span->start));
+               FreePageManagerPut(region->fpm, first_page, span->npages);
+               sb_free(span);
+
+               /* We're done, but must release any lock first. */
+               if (lock != NULL)
+                       LWLockRelease(lock);
+       }
+
        /* Put the object on the superblock's freelist. */
        superblock = relptr_access(base, span->start);
        Assert(((char *) ptr) >= superblock);
@@ -321,17 +336,17 @@ sb_free(void *ptr)
        span->firstfree = (((char *) ptr) - superblock) / obsize;
        span->nallocatable++;
 
-       /*
-        * If the superblock was previously completely full, we need to move it
-        * out of the highest-numbered fullness class so that it can potentially
-        * be used for new allocations.
-        */
        if (span->nallocatable == 1 && span->fclass == SB_FULLNESS_CLASSES - 1)
        {
                sb_heap *heap = relptr_access(base, span->parent);
                sb_span *new_nextspan;
 
-               /* Move to next lower-numbered list. */
+               /*
+                * The superblock is completely full and is located in the
+                * highest-numbered fullness class, which is never scanned for free
+                * chunks.  We must move it to the next-lower fullness class.
+                */
+
                sb_unlink_span(base, heap, span);
                span->fclass = SB_FULLNESS_CLASSES - 2;
                relptr_copy(span->nextspan, heap->spans[SB_FULLNESS_CLASSES - 2]);
@@ -342,11 +357,20 @@ sb_free(void *ptr)
                        relptr_store(base, new_nextspan->prevspan, span);
                relptr_store(base, heap->spans[SB_FULLNESS_CLASSES - 2], span);
        }
-       else if (span->nallocatable == span->nmax)
+       else if (span->nallocatable == span->nmax && (span->fclass != 1 ||
+               !relptr_is_null(span->prevspan)))
        {
                sb_heap *heap = relptr_access(base, span->parent);
                Size    first_page;
 
+               /*
+                * This entire superblock is free, and it's not the active superblock
+                * for this size class.  Return the memory to the free page manager.
+                * We don't do this for the active superblock to prevent hysteresis:
+                * if we repeatedly allocate and free the only chunk in the active
+                * superblock, it will be very inefficient if we deallocate and
+                * reallocate the superblock every time.
+                */
                sb_unlink_span(base, heap, span);
                first_page = fpm_pointer_to_page(fpm_base,
                                                                                 relptr_access(base, span->start));