diff --git a/py/asmthumb.c b/py/asmthumb.c
index a6fe4be809b2bdbc97677a0f62bd9acab5d66220..ee8041ac9f2bec7cd877b26253513f4b0913436e 100644
--- a/py/asmthumb.c
+++ b/py/asmthumb.c
@@ -44,7 +44,7 @@ asm_thumb_t *asm_thumb_new(uint max_num_labels) {
 
 void asm_thumb_free(asm_thumb_t *as, bool free_code) {
     if (free_code) {
-        m_free(as->code_base);
+        m_del(byte, as->code_base, as->code_size);
     }
     /*
     if (as->label != NULL) {
@@ -58,7 +58,7 @@ void asm_thumb_free(asm_thumb_t *as, bool free_code) {
         g_array_free(as->label, true);
     }
     */
-    m_free(as);
+    m_del_obj(asm_thumb_t, as);
 }
 
 void asm_thumb_start_pass(asm_thumb_t *as, int pass) {
diff --git a/py/asmx64.c b/py/asmx64.c
index c7e3cdc849182bc6ad06993d23d9f51afba7a735..2a11de207f894f2b9067b3822a7ba254e41fd8fe 100644
--- a/py/asmx64.c
+++ b/py/asmx64.c
@@ -128,7 +128,7 @@ void asm_x64_free(asm_x64_t* as, bool free_code) {
         g_array_free(as->label, true);
     }
     */
-    m_free(as);
+    m_del_obj(asm_x64_t, as);
 }
 
 void asm_x64_start_pass(asm_x64_t *as, int pass) {
diff --git a/py/builtin.c b/py/builtin.c
index 7d2cc72b77aa2b5fb3464902d7694894f5cba005..f6b57109c3934f3681d8bdbb94d43e7850c95d21 100644
--- a/py/builtin.c
+++ b/py/builtin.c
@@ -158,7 +158,7 @@ mp_obj_t mp_builtin_chr(mp_obj_t o_in) {
         char *str = m_new(char, 2);
         str[0] = ord;
         str[1] = '\0';
-        return mp_obj_new_str(qstr_from_str_take(str));
+        return mp_obj_new_str(qstr_from_str_take(str, 2));
     } else {
         nlr_jump(mp_obj_new_exception_msg(rt_q_ValueError, "chr() arg not in range(0x110000)"));
     }
diff --git a/py/compile.c b/py/compile.c
index 38f14155db484ffc55399123a5e78140d39dce06..604d5a24375f2f59162f78de8f0e3be67d5be865 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -1184,7 +1184,7 @@ void do_import_name(compiler_t *comp, mp_parse_node_t pn, qstr *q1, qstr *q2) {
                 }
                 strcat(str, qstr_str(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i])));
             }
-            *q2 = qstr_from_str_take(str);
+            *q2 = qstr_from_str_take(str, len + 1);
             EMIT(import_name, *q2);
             if (is_as) {
                 for (int i = 1; i < n; i++) {
@@ -2127,7 +2127,7 @@ void compile_atom_string(compiler_t *comp, mp_parse_node_struct_t *pns) {
         strcat(cat_str, str);
     }
 
-    EMIT(load_const_str, qstr_from_str_take(cat_str), string_kind == MP_PARSE_NODE_BYTES);
+    EMIT(load_const_str, qstr_from_str_take(cat_str, n_bytes + 1), string_kind == MP_PARSE_NODE_BYTES);
 }
 
 // pns needs to have 2 nodes, first is lhs of comprehension, second is PN_comp_for node
@@ -3105,7 +3105,7 @@ bool mp_compile(mp_parse_node_t pn, bool is_repl) {
         }
     }
 
-    m_free(comp);
+    m_del_obj(compiler_t, comp);
 
     return !comp->had_error;
 }
diff --git a/py/emitpass1.c b/py/emitpass1.c
index 551f30eb4c9cf46b4f1e6c15a65ed6ad01cf7e5f..4ed05497273645cb8cdf29aafd151427a2650a39 100644
--- a/py/emitpass1.c
+++ b/py/emitpass1.c
@@ -26,7 +26,7 @@ emit_t *emit_pass1_new(qstr qstr___class__) {
 }
 
 void emit_pass1_free(emit_t *emit) {
-    m_free(emit);
+    m_del_obj(emit_t, emit);
 }
 
 static void emit_pass1_dummy(emit_t *emit) {
diff --git a/py/lexer.c b/py/lexer.c
index cf9eae5531fe2829f096301cab21fe8609c3d97d..6e43c7469a1014c893215b2248b0040427c5d090 100644
--- a/py/lexer.c
+++ b/py/lexer.c
@@ -183,8 +183,8 @@ static void next_char(mp_lexer_t *lex) {
 
 void indent_push(mp_lexer_t *lex, uint indent) {
     if (lex->num_indent_level >= lex->alloc_indent_level) {
+        lex->indent_level = m_renew(uint16_t, lex->indent_level, lex->alloc_indent_level, lex->alloc_indent_level * 2);
         lex->alloc_indent_level *= 2;
-        lex->indent_level = m_renew(uint16_t, lex->indent_level, lex->alloc_indent_level);
     }
     lex->indent_level[lex->num_indent_level++] = indent;
 }
@@ -639,7 +639,7 @@ void mp_lexer_free(mp_lexer_t *lex) {
             lex->stream_close(lex->stream_data);
         }
         vstr_clear(&lex->vstr);
-        m_free(lex);
+        m_del_obj(mp_lexer_t, lex);
     }
 }
 
diff --git a/py/lexerunix.c b/py/lexerunix.c
index 80daf6009a5c82b04350495e37edfae32a617313..398cb792a7ace443dd3c712f0475fc12e35920dd 100644
--- a/py/lexerunix.c
+++ b/py/lexerunix.c
@@ -24,9 +24,9 @@ unichar str_buf_next_char(str_buf_t *sb) {
 void str_buf_free(str_buf_t *sb) {
     if (sb) {
         if (sb->free) {
-            m_free((char*)sb->src_beg);
+            m_del(char, (char*)sb->src_beg, 0 /* unknown size of src_beg */);
         }
-        m_free(sb);
+        m_del_obj(str_buf_t, sb);
     }
 }
 
@@ -52,7 +52,7 @@ mp_lexer_t *mp_lexer_new_from_file(const char *filename) {
     close(fd);
     if (read_size != size) {
         printf("error reading file %s\n", filename);
-        m_free(data);
+        m_del(char, data, size);
         return NULL;
     }
 
diff --git a/py/malloc.c b/py/malloc.c
index 2f8b5f78b5f95d597682791b215d2b85cd9d781d..c65d38a9687c43d96e76a0ddb85b176f4fafe991 100644
--- a/py/malloc.c
+++ b/py/malloc.c
@@ -5,12 +5,6 @@
 
 static int total_bytes_allocated = 0;
 
-void m_free(void *ptr) {
-    if (ptr != NULL) {
-        free(ptr);
-    }
-}
-
 void *m_malloc(int num_bytes) {
     if (num_bytes == 0) {
         return NULL;
@@ -37,20 +31,26 @@ void *m_malloc0(int num_bytes) {
     return ptr;
 }
 
-void *m_realloc(void *ptr, int num_bytes) {
-    if (num_bytes == 0) {
+void *m_realloc(void *ptr, int old_num_bytes, int new_num_bytes) {
+    if (new_num_bytes == 0) {
         free(ptr);
         return NULL;
     }
-    ptr = realloc(ptr, num_bytes);
+    ptr = realloc(ptr, new_num_bytes);
     if (ptr == NULL) {
-        printf("could not allocate memory, reallocating %d bytes\n", num_bytes);
+        printf("could not allocate memory, reallocating %d bytes\n", new_num_bytes);
         return NULL;
     }
-    total_bytes_allocated += num_bytes;
+    total_bytes_allocated += new_num_bytes;
     return ptr;
 }
 
+void m_free(void *ptr, int num_bytes) {
+    if (ptr != NULL) {
+        free(ptr);
+    }
+}
+
 int m_get_total_bytes_allocated(void) {
     return total_bytes_allocated;
 }
diff --git a/py/map.c b/py/map.c
index 3c2d1fbb5511174ec5eb9d103646fd775b06566b..01209c9b742690874565601722d553e186d293c3 100644
--- a/py/map.c
+++ b/py/map.c
@@ -64,7 +64,7 @@ mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_n
                             mp_map_lookup_helper(map, old_table[i].key, true)->value = old_table[i].value;
                         }
                     }
-                    m_free(old_table);
+                    m_del(mp_map_elem_t, old_table, old_alloc);
                     // restart the search for the new element
                     pos = hash % map->alloc;
                 } else {
@@ -124,7 +124,7 @@ mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, bool add_if_not_found) {
                             mp_set_lookup(set, old_table[i], true);
                         }
                     }
-                    m_free(old_table);
+                    m_del(mp_obj_t, old_table, old_alloc);
                     // restart the search for the new element
                     pos = hash % set->alloc;
                 } else {
diff --git a/py/misc.h b/py/misc.h
index 6c0e18ca0f8e23b87f41325e711fbdedce342591..1a33f0505d55a861474f73d20109a448d08a525a 100644
--- a/py/misc.h
+++ b/py/misc.h
@@ -16,16 +16,20 @@ typedef unsigned int uint;
 
 /** memomry allocation ******************************************/
 
+// TODO make a lazy m_renew that can increase by a smaller amount than requested (but by at least 1 more element)
+
 #define m_new(type, num) ((type*)(m_malloc(sizeof(type) * (num))))
 #define m_new0(type, num) ((type*)(m_malloc0(sizeof(type) * (num))))
-#define m_renew(type, ptr, num) ((type*)(m_realloc((ptr), sizeof(type) * (num))))
 #define m_new_obj(type) (m_new(type, 1))
 #define m_new_obj_var(obj_type, var_type, var_num) ((obj_type*)m_malloc(sizeof(obj_type) + sizeof(var_type) * (var_num)))
+#define m_renew(type, ptr, old_num, new_num) ((type*)(m_realloc((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num))))
+#define m_del(type, ptr, num) m_free(ptr, sizeof(type) * (num))
+#define m_del_obj(type, ptr) (m_del(type, ptr, 1))
 
-void m_free(void *ptr);
 void *m_malloc(int num_bytes);
 void *m_malloc0(int num_bytes);
-void *m_realloc(void *ptr, int num_bytes);
+void *m_realloc(void *ptr, int old_num_bytes, int new_num_bytes);
+void m_free(void *ptr, int num_bytes);
 
 int m_get_total_bytes_allocated(void);
 
@@ -96,7 +100,7 @@ typedef unsigned int qstr;
 
 void qstr_init(void);
 qstr qstr_from_str_static(const char *str);
-qstr qstr_from_str_take(char *str);
+qstr qstr_from_str_take(char *str, int alloc_len);
 qstr qstr_from_strn_copy(const char *str, int len);
 const char* qstr_str(qstr qstr);
 
diff --git a/py/objclass.c b/py/objclass.c
index 52e43724f7cc34eda66ddeb58843309fd5bc0269..a4bde3f0012f7f63277844c059abb74275c97a6d 100644
--- a/py/objclass.c
+++ b/py/objclass.c
@@ -37,7 +37,7 @@ mp_obj_t class_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
             memcpy(args2, args, n_args * sizeof(mp_obj_t));
             args2[n_args] = o;
             init_ret = rt_call_function_n(init_fn->value, n_args + 1, args2);
-            m_free(args2);
+            m_del(mp_obj_t, args2, n_args + 1);
         }
         if (init_ret != mp_const_none) {
             nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "__init__() should return None, not '%s'", mp_obj_get_type_str(init_ret)));
diff --git a/py/objfun.c b/py/objfun.c
index bf11b5e6ab71e696aa9eb9969c63d452fe62c10b..cefc9a95fe36888e4164dd4d3b36e523c51ac936 100644
--- a/py/objfun.c
+++ b/py/objfun.c
@@ -58,7 +58,7 @@ mp_obj_t fun_native_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
         }
 
         mp_obj_t res = ((mp_fun_var_t)self->fun)(n_args, args_ordered);
-        m_free(args_ordered);
+        m_del(mp_obj_t, args_ordered, n_args);
 
         return res;
     }
diff --git a/py/objlist.c b/py/objlist.c
index 4d70c37eb3ebab66003dc1c9dc94269635e79d98..ce16ab6fe941d33b1168bef3c9768772e306092e 100644
--- a/py/objlist.c
+++ b/py/objlist.c
@@ -57,8 +57,8 @@ mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg) {
     assert(MP_OBJ_IS_TYPE(self_in, &list_type));
     mp_obj_list_t *self = self_in;
     if (self->len >= self->alloc) {
+        self->items = m_renew(mp_obj_t, self->items, self->alloc, self->alloc * 2);
         self->alloc *= 2;
-        self->items = m_renew(mp_obj_t, self->items, self->alloc);
     }
     self->items[self->len++] = arg;
     return mp_const_none; // return None, as per CPython
diff --git a/py/objstr.c b/py/objstr.c
index d82ea3d3ce88a8b2ba01583ae18bc498a0671dea..104a54bb325aea36d506d7340ec145bb2028b619 100644
--- a/py/objstr.c
+++ b/py/objstr.c
@@ -36,9 +36,10 @@ mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
             if (MP_OBJ_IS_TYPE(rhs_in, &str_type)) {
                 // add 2 strings
                 const char *rhs_str = qstr_str(((mp_obj_str_t*)rhs_in)->qstr);
-                char *val = m_new(char, strlen(lhs_str) + strlen(rhs_str) + 1);
+                int alloc_len = strlen(lhs_str) + strlen(rhs_str) + 1;
+                char *val = m_new(char, alloc_len);
                 stpcpy(stpcpy(val, lhs_str), rhs_str);
-                return mp_obj_new_str(qstr_from_str_take(val));
+                return mp_obj_new_str(qstr_from_str_take(val, alloc_len));
             }
             break;
     }
@@ -78,7 +79,7 @@ mp_obj_t str_join(mp_obj_t self_in, mp_obj_t arg) {
         }
         strcat(joined_str, s2);
     }
-    return mp_obj_new_str(qstr_from_str_take(joined_str));
+    return mp_obj_new_str(qstr_from_str_take(joined_str, required_len + 1));
 
 bad_arg:
     nlr_jump(mp_obj_new_exception_msg(rt_q_TypeError, "?str.join expecting a list of str's"));
@@ -115,7 +116,7 @@ mp_obj_t str_format(int n_args, const mp_obj_t *args) {
         }
     }
 
-    return mp_obj_new_str(qstr_from_str_take(vstr->buf));
+    return mp_obj_new_str(qstr_from_str_take(vstr->buf, vstr->alloc));
 }
 
 static MP_DEFINE_CONST_FUN_OBJ_2(str_join_obj, str_join);
diff --git a/py/parse.c b/py/parse.c
index b939ab8db744829a91681a7c4ae8ffdf215a6a10..ad9a47ffbc57bff2ad36bbeeab4f1956d61b11c2 100644
--- a/py/parse.c
+++ b/py/parse.c
@@ -94,8 +94,8 @@ typedef struct _parser_t {
 
 static void push_rule(parser_t *parser, const rule_t *rule, int arg_i) {
     if (parser->rule_stack_top >= parser->rule_stack_alloc) {
+        parser->rule_stack = m_renew(rule_stack_t, parser->rule_stack, parser->rule_stack_alloc, parser->rule_stack_alloc * 2);
         parser->rule_stack_alloc *= 2;
-        parser->rule_stack = m_renew(rule_stack_t, parser->rule_stack, parser->rule_stack_alloc);
     }
     parser->rule_stack[parser->rule_stack_top].rule_id = rule->rule_id;
     parser->rule_stack[parser->rule_stack_top].arg_i = arg_i;
diff --git a/py/qstr.c b/py/qstr.c
index 6fa69ddb9586e2de390214946ac5d77a55fba5ec..0dd8a04b704d564b378f4110bc12afe7ffb4336c 100644
--- a/py/qstr.c
+++ b/py/qstr.c
@@ -16,8 +16,8 @@ void qstr_init(void) {
 
 static qstr qstr_add(const char *str) {
     if (qstrs_len >= qstrs_alloc) {
+        qstrs = m_renew(const char*, qstrs, qstrs_alloc, qstrs_alloc * 2);
         qstrs_alloc *= 2;
-        qstrs = m_renew(const char*, qstrs, qstrs_alloc);
     }
     qstrs[qstrs_len++] = str;
     return qstrs_len - 1;
@@ -32,10 +32,10 @@ qstr qstr_from_str_static(const char *str) {
     return qstr_add(str);
 }
 
-qstr qstr_from_str_take(char *str) {
+qstr qstr_from_str_take(char *str, int alloc_len) {
     for (int i = 0; i < qstrs_len; i++) {
         if (strcmp(qstrs[i], str) == 0) {
-            m_free(str);
+            m_del(char, str, alloc_len);
             return i;
         }
     }
diff --git a/py/scope.c b/py/scope.c
index 7d55d3923f002c5851af3213efb83646be99295c..38ea5a9e2f72096f1adffdcef0626fcaefd3f900 100644
--- a/py/scope.c
+++ b/py/scope.c
@@ -69,8 +69,8 @@ id_info_t *scope_find_or_add_id(scope_t *scope, qstr qstr, bool *added) {
 
     // make sure we have enough memory
     if (scope->id_info_len >= scope->id_info_alloc) {
+        scope->id_info = m_renew(id_info_t, scope->id_info, scope->id_info_alloc, scope->id_info_alloc * 2);
         scope->id_info_alloc *= 2;
-        scope->id_info = m_renew(id_info_t, scope->id_info, scope->id_info_alloc);
     }
 
     id_info_t *id_info;
diff --git a/py/vstr.c b/py/vstr.c
index f6652e6f10b97e423cd398f019e71ee2afb582ae..98cf0272505f516e6e654cdbfab15b92b62dd45a 100644
--- a/py/vstr.c
+++ b/py/vstr.c
@@ -11,7 +11,6 @@ void vstr_init(vstr_t *vstr) {
     vstr->len = 0;
     vstr->buf = m_new(char, vstr->alloc);
     if (vstr->buf == NULL) {
-        m_free(vstr);
         vstr->had_error = true;
         return;
     }
@@ -20,7 +19,7 @@ void vstr_init(vstr_t *vstr) {
 }
 
 void vstr_clear(vstr_t *vstr) {
-    m_free(vstr->buf);
+    m_del(char, vstr->buf, vstr->alloc);
     vstr->buf = NULL;
 }
 
@@ -35,8 +34,8 @@ vstr_t *vstr_new(void) {
 
 void vstr_free(vstr_t *vstr) {
     if (vstr != NULL) {
-        m_free(vstr->buf);
-        m_free(vstr);
+        m_del(char, vstr->buf, vstr->alloc);
+        m_del_obj(vstr_t, vstr);
     }
 }
 
@@ -67,7 +66,7 @@ int vstr_len(vstr_t *vstr) {
 bool vstr_ensure_extra(vstr_t *vstr, int size) {
     if (vstr->len + size + 1 > vstr->alloc) {
         int new_alloc = ROUND_ALLOC((vstr->len + size + 1) * 2);
-        char *new_buf = m_renew(char, vstr->buf, new_alloc);
+        char *new_buf = m_renew(char, vstr->buf, vstr->alloc, new_alloc);
         if (new_buf == NULL) {
             vstr->had_error = true;
             return false;
diff --git a/stm/lexerstm.c b/stm/lexerstm.c
index 4e99052242230f5c913b9e2baa717ac8cf23888b..dfb84cca137370f452ca8260345cba7f23129d69 100644
--- a/stm/lexerstm.c
+++ b/stm/lexerstm.c
@@ -17,7 +17,7 @@ unichar str_buf_next_char(mp_lexer_str_buf_t *sb) {
 
 void str_buf_free(mp_lexer_str_buf_t *sb) {
     if (sb->free) {
-        m_free((char*)sb->src_beg);
+        m_del(char, (char*)sb->src_beg, 0 /* don't know allocated size of src */);
     }
 }
 
diff --git a/stm/main.c b/stm/main.c
index 22ce46f256e9b45d627c814bafc64c55156de3c8..8c2c96af2aa60de3c486cf55c5b3209577c49225 100644
--- a/stm/main.c
+++ b/stm/main.c
@@ -706,7 +706,7 @@ mp_obj_t file_obj_read(mp_obj_t self_in, mp_obj_t arg) {
     UINT n_out;
     f_read(&self->fp, buf, n, &n_out);
     buf[n_out] = 0;
-    return mp_obj_new_str(qstr_from_str_take(buf));
+    return mp_obj_new_str(qstr_from_str_take(buf, n + 1));
 }
 
 mp_obj_t file_obj_write(mp_obj_t self_in, mp_obj_t arg) {
diff --git a/stm/pybwlan.c b/stm/pybwlan.c
index c078ecfe14376e48f7fd9d0759b16894aa8e9259..73aa8273ffd07cd527f3d7f2ade4fb96f4c6102c 100644
--- a/stm/pybwlan.c
+++ b/stm/pybwlan.c
@@ -191,7 +191,7 @@ mp_obj_t pyb_wlan_http_get(mp_obj_t host_name, mp_obj_t host_path) {
             vstr_add_strn(vstr, buf, ret);
         }
 
-        mp_ret = mp_obj_new_str(qstr_from_str_take(vstr_str(vstr)));
+        mp_ret = mp_obj_new_str(qstr_from_str_take(vstr->buf, vstr->alloc));
     }
 
     closesocket(sd);
diff --git a/unix/main.c b/unix/main.c
index 16012e71811a5c55071d11ce9f8988211626a528..73da1ecfc2f361e4b49054934f0e8c8175de96d7 100644
--- a/unix/main.c
+++ b/unix/main.c
@@ -1,6 +1,7 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
+#include <malloc.h>
 
 #include "nlr.h"
 #include "misc.h"
@@ -44,8 +45,8 @@ static void do_repl(void) {
                     break;
                 }
                 char *line3 = str_join(line, '\n', line2);
-                m_free(line);
-                m_free(line2);
+                free(line);
+                free(line2);
                 line = line3;
             }
         }