diff --git a/array.c b/array.c
index 76289f4..1e1bd5b 100644
--- a/array.c
+++ b/array.c
@@ -210,6 +210,7 @@
             ARY_SET_HEAP_LEN(ary, len);
         }
         else {
+            xwillfree(RARRAY(ary)->as.heap.aux.capa * sizeof(VALUE));
             REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, (capacity));
         }
         ARY_SET_CAPA(ary, (capacity));
@@ -218,6 +219,7 @@
         if (!ARY_EMBED_P(ary)) {
             long len = RARRAY_LEN(ary);
 	    const VALUE *ptr = RARRAY_CONST_PTR(ary);
+            xwillfree(RARRAY(ary)->as.heap.aux.capa * sizeof(VALUE));
             if (len > capacity) len = capacity;
             MEMCPY((VALUE *)RARRAY(ary)->as.ary, ptr, VALUE, len);
             FL_SET_EMBED(ary);
@@ -234,8 +236,10 @@
     long old_capa = RARRAY(ary)->as.heap.aux.capa;
     assert(!ARY_SHARED_P(ary));
     assert(old_capa >= capacity);
-    if (old_capa > capacity)
+    if (old_capa > capacity) {
+	xwillfree(old_capa * sizeof(VALUE));
 	REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, capacity);
+    }
 }
 
 static void
@@ -533,6 +537,7 @@
 rb_ary_free(VALUE ary)
 {
     if (ARY_OWNS_HEAP_P(ary)) {
+	xwillfree(RARRAY(ary)->as.heap.aux.capa * sizeof(VALUE));
 	xfree((void *)ARY_HEAP_PTR(ary));
     }
 }
@@ -712,6 +717,7 @@
     rb_ary_modify(ary);
     if (argc == 0) {
 	if (ARY_OWNS_HEAP_P(ary) && RARRAY_CONST_PTR(ary) != 0) {
+	    xwillfree(RARRAY(ary)->as.heap.aux.capa * sizeof(VALUE));
 	    xfree((void *)RARRAY_CONST_PTR(ary));
 	}
         rb_ary_unshare_safe(ary);
@@ -1633,6 +1639,7 @@ enum ary_take_pos_flags
     }
     else {
 	if (olen > len + ARY_DEFAULT_SIZE) {
+	    xwillfree(RARRAY(ary)->as.heap.aux.capa * sizeof(VALUE));
 	    REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, len);
 	    ARY_SET_CAPA(ary, len);
 	}
@@ -2420,6 +2427,7 @@ enum {
                     rb_ary_unshare(ary);
                 }
                 else {
+		    xwillfree(RARRAY(ary)->as.heap.aux.capa * sizeof(VALUE));
 		    xfree((void *)ARY_HEAP_PTR(ary));
                 }
                 ARY_SET_PTR(ary, RARRAY_CONST_PTR(tmp));
@@ -3284,6 +3292,7 @@ enum {
         VALUE shared = 0;
 
         if (ARY_OWNS_HEAP_P(copy)) {
+	    xwillfree(RARRAY(copy)->as.heap.aux.capa * sizeof(VALUE));
 	    RARRAY_PTR_USE(copy, ptr, xfree(ptr));
 	}
         else if (ARY_SHARED_P(copy)) {
@@ -3300,6 +3309,7 @@ enum {
     else {
         VALUE shared = ary_make_shared(orig);
         if (ARY_OWNS_HEAP_P(copy)) {
+	    xwillfree(RARRAY(copy)->as.heap.aux.capa * sizeof(VALUE));
 	    RARRAY_PTR_USE(copy, ptr, xfree(ptr));
         }
         else {
diff --git a/gc.c b/gc.c
index fae65e1..426809a 100644
--- a/gc.c
+++ b/gc.c
@@ -90,7 +90,7 @@
 #define GC_MALLOC_LIMIT (8 /* 8 MB */ * 1024 * 1024 /* 1MB */)
 #endif
 #ifndef GC_MALLOC_LIMIT_MAX
-#define GC_MALLOC_LIMIT_MAX (512 /* 512 MB */ * 1024 * 1024 /* 1MB */)
+#define GC_MALLOC_LIMIT_MAX (256 /* 256 MB */ * 1024 * 1024 /* 1MB */)
 #endif
 #ifndef GC_MALLOC_LIMIT_GROWTH_FACTOR
 #define GC_MALLOC_LIMIT_GROWTH_FACTOR 1.8
@@ -317,8 +317,8 @@ struct gc_list {
 
 typedef struct rb_objspace {
     struct {
-	size_t limit;
-	size_t increase;
+	ssize_t limit;
+	ssize_t increase;
 #if CALC_EXACT_MALLOC_SIZE
 	size_t allocated_size;
 	size_t allocations;
@@ -1257,6 +1257,7 @@ struct heap_slot {
       case T_OBJECT:
 	if (!(RANY(obj)->as.basic.flags & ROBJECT_EMBED) &&
             RANY(obj)->as.object.as.heap.ivptr) {
+	    xwillfree(RANY(obj)->as.object.as.heap.numiv * sizeof(VALUE));
 	    xfree(RANY(obj)->as.object.as.heap.ivptr);
 	}
 	break;
@@ -1354,6 +1355,7 @@ struct heap_slot {
 
       case T_BIGNUM:
 	if (!(RBASIC(obj)->flags & RBIGNUM_EMBED_FLAG) && RBIGNUM_DIGITS(obj)) {
+	    xwillfree(RANY(obj)->as.bignum.as.heap.len * sizeof(BDIGIT));
 	    xfree(RBIGNUM_DIGITS(obj));
 	}
 	break;
@@ -1378,6 +1380,7 @@ struct heap_slot {
       case T_STRUCT:
 	if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) == 0 &&
 	    RANY(obj)->as.rstruct.as.heap.ptr) {
+	    xwillfree(RANY(obj)->as.rstruct.as.heap.len * sizeof(VALUE));
 	    xfree((void *)RANY(obj)->as.rstruct.as.heap.ptr);
 	}
 	break;
@@ -2415,8 +2418,8 @@ struct force_finalize_list {
 
     /* reset malloc info */
     {
-	size_t inc = ATOMIC_SIZE_EXCHANGE(malloc_increase, 0);
-	size_t old_limit = malloc_limit;
+	ssize_t inc = ATOMIC_SIZE_EXCHANGE(malloc_increase, 0);
+	ssize_t old_limit = malloc_limit;
 
 	if (inc > malloc_limit) {
 	    malloc_limit += (size_t)(malloc_limit * (initial_malloc_limit_growth_factor - 1));
@@ -4826,16 +4829,22 @@ struct objspace_and_reason {
 }
 
 static void
-vm_malloc_increase(rb_objspace_t *objspace, size_t size, int do_gc)
+vm_malloc_increase(rb_objspace_t *objspace, size_t size)
 {
     ATOMIC_SIZE_ADD(malloc_increase, size);
 
     if ((ruby_gc_stress && !ruby_disable_gc_stress) ||
-	(do_gc && (malloc_increase > malloc_limit))) {
+	(malloc_increase > malloc_limit)) {
 	garbage_collect_with_gvl(objspace, 0, 0, GPR_FLAG_MALLOC);
     }
 }
 
+static void
+vm_malloc_decrease(rb_objspace_t *objspace, size_t size)
+{
+	ATOMIC_SIZE_SUB(malloc_increase, size);
+}
+
 static inline size_t
 vm_malloc_prepare(rb_objspace_t *objspace, size_t size)
 {
@@ -4848,7 +4857,7 @@ struct objspace_and_reason {
     size += sizeof(size_t);
 #endif
 
-    vm_malloc_increase(objspace, size, TRUE);
+    vm_malloc_increase(objspace, size);
 
     return size;
 }
@@ -4908,7 +4917,7 @@ struct objspace_and_reason {
 	return 0;
     }
 
-    vm_malloc_increase(objspace, size, FALSE);
+    vm_malloc_increase(objspace, size);
 
 #if CALC_EXACT_MALLOC_SIZE
     size += sizeof(size_t);
@@ -5007,6 +5016,11 @@ struct objspace_and_reason {
 	vm_xfree(&rb_objspace, x);
 }
 
+void
+ruby_xwill_free(ssize_t size)
+{
+    vm_malloc_decrease(&rb_objspace, size);
+}
 
 /* Mimic ruby_xmalloc, but need not rb_objspace.
  * should return pointer suitable for ruby_xfree
diff --git a/include/ruby/defines.h b/include/ruby/defines.h
index 3327e69..3ff2b0c 100644
--- a/include/ruby/defines.h
+++ b/include/ruby/defines.h
@@ -111,6 +111,7 @@
 #define xrealloc ruby_xrealloc
 #define xrealloc2 ruby_xrealloc2
 #define xfree ruby_xfree
+#define xwillfree ruby_xwill_free
 
 #if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3
 # define RUBY_ATTR_ALLOC_SIZE(params) __attribute__ ((__alloc_size__ params))
@@ -124,6 +125,7 @@
 void *xrealloc(void*,size_t) RUBY_ATTR_ALLOC_SIZE((2));
 void *xrealloc2(void*,size_t,size_t) RUBY_ATTR_ALLOC_SIZE((2,3));
 void xfree(void*);
+void xwillfree(ssize_t);
 
 #define STRINGIZE(expr) STRINGIZE0(expr)
 #ifndef STRINGIZE0
diff --git a/st.c b/st.c
index 6e3df62..753ced5 100644
--- a/st.c
+++ b/st.c
@@ -78,6 +78,9 @@ struct st_table_entry {
 #define calloc xcalloc
 #define realloc xrealloc
 #define free(x) xfree(x)
+#define willfree(x) xwillfree(x)
+#else
+#define willfree(x)
 #endif
 
 #define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
@@ -89,11 +92,11 @@ struct st_table_entry {
 
 /* preparation for possible allocation improvements */
 #define st_alloc_entry() (st_table_entry *)malloc(sizeof(st_table_entry))
-#define st_free_entry(entry) free(entry)
+#define st_free_entry(entry) do { xwillfree(sizeof(*entry)); free(entry); } while(0)
 #define st_alloc_table() (st_table *)malloc(sizeof(st_table))
-#define st_dealloc_table(table) free(table)
+#define st_dealloc_table(table) do { xwillfree(sizeof(*table)); free(table); } while(0)
 #define st_alloc_bins(size) (st_table_entry **)calloc(size, sizeof(st_table_entry *))
-#define st_free_bins(bins, size) free(bins)
+#define st_free_bins(bins, size) do { xwillfree(size*sizeof(*bins)); free(bins); } while(0)
 static inline st_table_entry**
 st_realloc_bins(st_table_entry **bins, st_index_t newsize, st_index_t oldsize)
 {
diff --git a/string.c b/string.c
index 7bf4006..3dcfa23 100644
--- a/string.c
+++ b/string.c
@@ -115,6 +115,7 @@
 	}\
     }\
     else {\
+	xwillfree(RSTRING(str)->as.heap.aux.capa);\
 	REALLOC_N(RSTRING(str)->as.heap.ptr, char, (capacity)+termlen);\
 	if (!STR_NOCAPA_P(str))\
 	    RSTRING(str)->as.heap.aux.capa = (capacity);\
@@ -877,6 +878,7 @@
 	st_delete(frozen_strings, &fstr, NULL);
     }
     if (!STR_EMBED_P(str) && !STR_SHARED_P(str)) {
+	xwillfree(RSTRING(str)->as.heap.aux.capa);
 	xfree(RSTRING(str)->as.heap.ptr);
     }
 }
@@ -1434,6 +1436,7 @@
 	long capa = len + expand;
 	int termlen = TERM_LEN(str);
 	if (!STR_EMBED_P(str)) {
+	    xwillfree(RSTRING(str)->as.heap.aux.capa);
 	    REALLOC_N(RSTRING(str)->as.heap.ptr, char, capa + termlen);
 	    RSTRING(str)->as.heap.aux.capa = capa;
 	}
@@ -1460,6 +1463,7 @@
 {
     str_modifiable(str);
     if (!STR_SHARED_P(str) && !STR_EMBED_P(str)) {
+	xwillfree(RSTRING(str)->as.heap.aux.capa);
 	xfree(RSTRING_PTR(str));
 	RSTRING(str)->as.heap.ptr = 0;
 	RSTRING(str)->as.heap.len = 0;
@@ -1978,18 +1982,23 @@
 	}
 	else if (len + termlen <= RSTRING_EMBED_LEN_MAX + 1) {
 	    char *ptr = RSTRING(str)->as.heap.ptr;
+            ssize_t oldlen = RSTRING(str)->as.heap.aux.capa;
 	    STR_SET_EMBED(str);
 	    if (slen > len) slen = len;
 	    if (slen > 0) MEMCPY(RSTRING(str)->as.ary, ptr, char, slen);
 	    TERM_FILL(RSTRING(str)->as.ary + len, termlen);
 	    STR_SET_EMBED_LEN(str, len);
-	    if (independent) xfree(ptr);
+	    if (independent) {
+                xwillfree(oldlen);
+                xfree(ptr);
+            }
 	    return str;
 	}
 	else if (!independent) {
 	    str_make_independent_expand(str, len - slen);
 	}
 	else if (slen < len || slen - len > 1024) {
+	    xwillfree(RSTRING(str)->as.heap.aux.capa);
 	    REALLOC_N(RSTRING(str)->as.heap.ptr, char, len + termlen);
 	}
 	if (!STR_NOCAPA_P(str)) {
@@ -3571,11 +3580,15 @@ enum neighbor_char {
     if (nlen <= RSTRING_EMBED_LEN_MAX) {
 	char *oldptr = ptr;
 	int fl = (int)(RBASIC(str)->flags & (STR_NOEMBED|ELTS_SHARED));
+        ssize_t oldlen = RSTRING(str)->as.heap.aux.capa;
 	STR_SET_EMBED(str);
 	STR_SET_EMBED_LEN(str, nlen);
 	ptr = RSTRING(str)->as.ary;
 	memmove(ptr, oldptr + len, nlen);
-	if (fl == STR_NOEMBED) xfree(oldptr);
+	if (fl == STR_NOEMBED) {
+            xwillfree(oldlen);
+            xfree(oldptr);
+        }
     }
     else {
 	if (!STR_SHARED_P(str)) rb_str_new4(str);
@@ -5494,6 +5507,7 @@ struct tr {
 	    t += tlen;
 	}
 	if (!STR_EMBED_P(str)) {
+	    xwillfree(RSTRING(str)->as.heap.aux.capa);
 	    xfree(RSTRING(str)->as.heap.ptr);
 	}
 	*t = '\0';
@@ -5570,6 +5584,7 @@ struct tr {
 	    t += tlen;
 	}
 	if (!STR_EMBED_P(str)) {
+	    xwillfree(RSTRING(str)->as.heap.aux.capa);
 	    xfree(RSTRING(str)->as.heap.ptr);
 	}
 	*t = '\0';
diff --git a/time.c b/time.c
index 2c83aa7..e624d27 100644
--- a/time.c
+++ b/time.c
@@ -1784,7 +1784,10 @@ struct time_object {
 static void
 time_free(void *tobj)
 {
-    if (tobj) xfree(tobj);
+    if (tobj) {
+        xwillfree(sizeof(struct time_object));
+        xfree(tobj);
+    }
 }
 
 static size_t
diff --git a/variable.c b/variable.c
index 3b3189b..129bc17 100644
--- a/variable.c
+++ b/variable.c
@@ -1171,6 +1171,7 @@ struct trace_data {
                     ROBJECT(obj)->as.heap.ivptr = newptr;
                 }
                 else {
+                    xwillfree(ROBJECT(obj)->as.heap.numiv * sizeof(VALUE));
                     REALLOC_N(ROBJECT(obj)->as.heap.ivptr, VALUE, newsize);
                     newptr = ROBJECT(obj)->as.heap.ivptr;
                 }
