diff --git a/core/alloctracker.cc b/core/alloctracker.cc
index 7ff2270b61040208ac6428ebea02407b71c98f8d..b2033f67852fa6f5c0949c255a79fd17b6e6859b 100644
--- a/core/alloctracker.cc
+++ b/core/alloctracker.cc
@@ -6,49 +6,49 @@
 
 namespace memory {
 
+__thread alloc_tracker::in_tracker_t alloc_tracker::in_tracker;
+
 void alloc_tracker::remember(void *addr, int size)
 {
-    std::lock_guard<mutex> guard(lock);
-    // If the code in this method causes new allocations, they will in turn
-    // cause a nested call to this function. To avoid endless recursion, we
-    // do not track these deeper memory allocations. Remember that the
-    // purpose of the allocation tracking is finding memory leaks in the
-    // application code, not in the code in this file.
-    if (lock.getdepth() > 1) {
+    if (in_tracker)
         return;
-    }
+    std::lock_guard<in_tracker_t> intracker(in_tracker);
 
-    if (!allocations || first_free < 0) {
-        // Grow the vector to make room for more allocation records.
-        int old_size = size_allocations;
-        size_allocations = size_allocations ? 2*size_allocations : 1024;
-        struct alloc_info *old_allocations = allocations;
-        allocations = (struct alloc_info *)
-                malloc(size_allocations * sizeof(struct alloc_info));
-        if (old_allocations) {
-            memcpy(allocations, old_allocations,
-                size_allocations * sizeof(struct alloc_info));
-        } else {
-            first_free = -1;
-            newest_allocation = -1;
-        }
-        for (size_t i = old_size; i < size_allocations; ++i) {
-            allocations[i].addr = 0;
-            allocations[i].next = first_free;
-            first_free = i;
+    int index;
+
+    WITH_LOCK(lock) {
+        if (!allocations || first_free < 0) {
+            // Grow the vector to make room for more allocation records.
+            int old_size = size_allocations;
+            size_allocations = size_allocations ? 2*size_allocations : 1024;
+            struct alloc_info *old_allocations = allocations;
+            allocations = (struct alloc_info *)
+                         malloc(size_allocations * sizeof(struct alloc_info));
+            if (old_allocations) {
+                memcpy(allocations, old_allocations,
+                        size_allocations * sizeof(struct alloc_info));
+            } else {
+                first_free = -1;
+                newest_allocation = -1;
+            }
+            for (size_t i = old_size; i < size_allocations; ++i) {
+                allocations[i].addr = 0;
+                allocations[i].next = first_free;
+                first_free = i;
+            }
         }
-    }
 
-    // Free node available, reuse it
-    int index = first_free;
-    struct alloc_info *a = &allocations[index];
-    first_free = a->next;
-    a->next = newest_allocation;
-    newest_allocation = index;
+        // Free node available, reuse it
+        index = first_free;
+        struct alloc_info *a = &allocations[index];
+        first_free = a->next;
+        a->next = newest_allocation;
+        newest_allocation = index;
 
-    a->seq = current_seq++;
-    a->addr = addr;
-    a->size = size;
+        a->seq = current_seq++;
+        a->addr = addr;
+        a->size = size;
+    }
 
     // Do the backtrace. If we ask for only a small number of call levels
     // we'll get only the deepest (most recent) levels, but when
@@ -71,9 +71,15 @@ void alloc_tracker::remember(void *addr, int size)
         bt_from += n - MAX_BACKTRACE;
         n = MAX_BACKTRACE;
     }
-    a->nbacktrace = n < 0 ? 0 : n;
-    for (int i = 0; i < n; i++) {
-        a->backtrace[i] = bt_from[i];
+
+    // We need the lock back, to prevent a concurrent allocation from moving
+    // allocations[] while we use it.
+    WITH_LOCK(lock) {
+        struct alloc_info *a = &allocations[index];
+        a->nbacktrace = n < 0 ? 0 : n;
+        for (int i = 0; i < n; i++) {
+            a->backtrace[i] = bt_from[i];
+        }
     }
 }
 
@@ -81,10 +87,10 @@ void alloc_tracker::forget(void *addr)
 {
     if (!addr)
         return;
-    std::lock_guard<mutex> guard(lock);
-    if (lock.getdepth() > 1) {
+    if (in_tracker)
         return;
-    }
+    std::lock_guard<in_tracker_t> intracker(in_tracker);
+    std::lock_guard<mutex> guard(lock);
     if (!allocations) {
         return;
     }
diff --git a/include/alloctracker.hh b/include/alloctracker.hh
index d4d19b7afd67e33333191e08d6f4e3ed005f91c6..99487003e27f6ecb686b9b320a1d4b5fd5c29c17 100644
--- a/include/alloctracker.hh
+++ b/include/alloctracker.hh
@@ -68,6 +68,28 @@ private:
     size_t size_allocations = 0;
     unsigned long current_seq = 0;
     mutex lock;
+
+    // If the allocation tracking code causes new allocations, they will in
+    // turn cause a nested call to the tracking functions. To avoid endless
+    // recursion, we do not track these deeper memory allocations. Remember
+    // that the purpose of the allocation tracking is finding memory leaks in
+    // the application code, not allocation tracker code.
+    class in_tracker_t {
+    private:
+        bool flag;
+    public:
+        void lock() {
+            flag = true;
+        }
+        void unlock() {
+            flag = false;
+        }
+        operator bool() {
+            return flag;
+        }
+    };
+    static __thread class in_tracker_t in_tracker;
+
 };
 #endif