diff --git a/core/mempool.cc b/core/mempool.cc
index 417e932f02833dfd3cfce252108cb0ae6fd12b6c..638ee4d57cc4ea1449e8761e41ac707f303df6b4 100644
--- a/core/mempool.cc
+++ b/core/mempool.cc
@@ -228,23 +228,33 @@ void pool::add_page()
     });
 }
 
+inline bool pool::have_full_pages()
+{
+    return !_free->empty() && _free->back().nalloc == 0;
+}
+
 void pool::free_same_cpu(free_object* obj, unsigned cpu_id)
 {
     void* object = static_cast<void*>(obj);
     trace_pool_free_same_cpu(this, object);
 
     page_header* header = to_header(obj);
-    if (!--header->nalloc) {
+    if (!--header->nalloc && have_full_pages()) {
         if (header->local_free) {
             _free->erase(_free->iterator_to(*header));
         }
-        // FIXME: add hysteresis
         sched::preempt_enable();
         untracked_free_page(header);
         sched::preempt_disable();
     } else {
         if (!header->local_free) {
-            _free->push_front(*header);
+            if (header->nalloc) {
+                _free->push_front(*header);
+            } else {
+                // keep full pages on the back, so they're not fragmented
+                // early, and so we find them easily in have_full_pages()
+                _free->push_back(*header);
+            }
         }
         obj->next = header->local_free;
         header->local_free = obj;
diff --git a/include/mempool.hh b/include/mempool.hh
index fc3b45f43e881b4033879b9c82d1897541080f76..7badf79d71c3888f6fc3bbce0c8dfd9cbac9bf71 100644
--- a/include/mempool.hh
+++ b/include/mempool.hh
@@ -39,6 +39,7 @@ private:
     struct page_header;
     struct free_object;
 private:
+    bool have_full_pages();
     void add_page();
     static page_header* to_header(free_object* object);