diff --git a/src/core/or/circuitlist.c b/src/core/or/circuitlist.c
index 7cf5e3445ac691aae3c43cd62e517f64196d53b9..088540dea22e759dde4633d2fa4f9ed849bc748e 100644
--- a/src/core/or/circuitlist.c
+++ b/src/core/or/circuitlist.c
@@ -1081,9 +1081,9 @@ circuit_free_(circuit_t *circ)
     circuit_remove_from_origin_circuit_list(ocirc);
 
     if (ocirc->half_streams) {
-      SMARTLIST_FOREACH_BEGIN(ocirc->half_streams, half_edge_t*,
+      SMARTLIST_FOREACH_BEGIN(ocirc->half_streams, half_edge_t *,
                               half_conn) {
-          tor_free(half_conn);
+        half_edge_free(half_conn);
       } SMARTLIST_FOREACH_END(half_conn);
       smartlist_free(ocirc->half_streams);
     }
@@ -2421,6 +2421,20 @@ n_cells_in_circ_queues(const circuit_t *c)
   return n;
 }
 
+/** Return the number of bytes allocated for <b>c</c>'s half-open streams. */
+static size_t
+circuit_alloc_in_half_streams(const circuit_t *c)
+{
+  if (! CIRCUIT_IS_ORIGIN(c)) {
+    return 0;
+  }
+  const origin_circuit_t *ocirc = CONST_TO_ORIGIN_CIRCUIT(c);
+  if (ocirc->half_streams)
+    return smartlist_len(ocirc->half_streams) * sizeof(half_edge_t);
+  else
+    return 0;
+}
+
 /**
  * Return the age of the oldest cell queued on <b>c</b>, in timestamp units.
  * Return 0 if there are no cells queued on c.  Requires that <b>now</b> be
@@ -2655,6 +2669,7 @@ circuits_handle_oom(size_t current_allocation)
 
     /* Now, kill the circuit. */
     n = n_cells_in_circ_queues(circ);
+    const size_t half_stream_alloc = circuit_alloc_in_half_streams(circ);
     if (! circ->marked_for_close) {
       circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
     }
@@ -2664,6 +2679,7 @@ circuits_handle_oom(size_t current_allocation)
     ++n_circuits_killed;
 
     mem_recovered += n * packed_cell_mem_cost();
+    mem_recovered += half_stream_alloc;
     mem_recovered += freed;
 
     if (mem_recovered >= mem_to_recover)
diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c
index 35e68485b8b9d955b1a0caf295e1a62bc6670189..d49e04021963172244be081e2cc46027b99abdd9 100644
--- a/src/core/or/connection_edge.c
+++ b/src/core/or/connection_edge.c
@@ -520,6 +520,9 @@ connection_half_edge_compare_bsearch(const void *key, const void **member)
   return *(const streamid_t*)key - e2->stream_id;
 }
 
+/** Total number of half_edge_t objects allocated */
+static size_t n_half_conns_allocated = 0;
+
 /**
  * Add a half-closed connection to the list, to watch for activity.
  *
@@ -544,6 +547,7 @@ connection_half_edge_add(const edge_connection_t *conn,
   }
 
   half_conn = tor_malloc_zero(sizeof(half_edge_t));
+  ++n_half_conns_allocated;
 
   if (!circ->half_streams) {
     circ->half_streams = smartlist_new();
@@ -573,6 +577,23 @@ connection_half_edge_add(const edge_connection_t *conn,
   smartlist_insert(circ->half_streams, insert_at, half_conn);
 }
 
+/** Release space held by <b>he</b> */
+void
+half_edge_free_(half_edge_t *he)
+{
+  if (!he)
+    return;
+  --n_half_conns_allocated;
+  tor_free(he);
+}
+
+/** Return the number of bytes devoted to storing info on half-open streams. */
+size_t
+half_streams_get_total_allocation(void)
+{
+  return n_half_conns_allocated * sizeof(half_edge_t);
+}
+
 /**
  * Find a stream_id_t in the list in O(lg(n)).
  *
@@ -693,7 +714,7 @@ connection_half_edge_is_valid_end(smartlist_t *half_conns,
 
   half = smartlist_get(half_conns, remove_idx);
   smartlist_del_keeporder(half_conns, remove_idx);
-  tor_free(half);
+  half_edge_free(half);
   return 1;
 }
 
diff --git a/src/core/or/connection_edge.h b/src/core/or/connection_edge.h
index 1348dd49f9dd2b53437b98be6b611a054e7af7b5..71ea95d6e5613adf6054f39a2cd612c86377bd61 100644
--- a/src/core/or/connection_edge.h
+++ b/src/core/or/connection_edge.h
@@ -185,6 +185,12 @@ int connection_half_edge_is_valid_end(smartlist_t *half_conns,
 int connection_half_edge_is_valid_resolved(smartlist_t *half_conns,
                                            streamid_t stream_id);
 
+size_t half_streams_get_total_allocation(void);
+struct half_edge_t;
+void half_edge_free_(struct half_edge_t *he);
+#define half_edge_free(he) \
+  FREE_AND_NULL(half_edge_t, half_edge_free_, (he))
+
 /** @name Begin-cell flags
  *
  * These flags are used in RELAY_BEGIN cells to change the default behavior
diff --git a/src/core/or/relay.c b/src/core/or/relay.c
index 260d84797cdcb8d50f5248b82cd1a600d0b043d2..2c77abbeece39604878de712c8e534ca33095a28 100644
--- a/src/core/or/relay.c
+++ b/src/core/or/relay.c
@@ -2604,6 +2604,7 @@ cell_queues_check_size(void)
 {
   time_t now = time(NULL);
   size_t alloc = cell_queues_get_total_allocation();
+  alloc += half_streams_get_total_allocation();
   alloc += buf_get_total_allocation();
   alloc += tor_compress_get_total_allocation();
   const size_t rend_cache_total = rend_cache_get_total_allocation();