Remove ratio-based purging.

Make decay-based purging the default (and only) mode.

Remove associated mallctls:
- opt.purge
- opt.lg_dirty_mult
- arena.<i>.lg_dirty_mult
- arenas.lg_dirty_mult
- stats.arenas.<i>.lg_dirty_mult

This resolves #385.
diff --git a/Makefile.in b/Makefile.in
index ec86307..5feb71d 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -374,17 +374,15 @@
 check_dir: check_unit_dir check_integration_dir
 
 check_unit: tests_unit check_unit_dir
-	$(MALLOC_CONF)="purge:ratio" $(SHELL) $(objroot)test/test.sh $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%)
-	$(MALLOC_CONF)="purge:decay" $(SHELL) $(objroot)test/test.sh $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%)
+	$(SHELL) $(objroot)test/test.sh $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%)
 check_integration_prof: tests_integration check_integration_dir
 ifeq ($(enable_prof), 1)
 	$(MALLOC_CONF)="prof:true" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%)
 	$(MALLOC_CONF)="prof:true,prof_active:false" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%)
 endif
 check_integration_decay: tests_integration check_integration_dir
-	$(MALLOC_CONF)="purge:decay,decay_time:-1" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%)
-	$(MALLOC_CONF)="purge:decay,decay_time:0" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%)
-	$(MALLOC_CONF)="purge:decay" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%)
+	$(MALLOC_CONF)="decay_time:-1" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%)
+	$(MALLOC_CONF)="decay_time:0" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%)
 check_integration: tests_integration check_integration_dir
 	$(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%)
 stress: tests_stress stress_dir
diff --git a/doc/jemalloc.xml.in b/doc/jemalloc.xml.in
index 8000461..f5a7247 100644
--- a/doc/jemalloc.xml.in
+++ b/doc/jemalloc.xml.in
@@ -924,42 +924,6 @@
         number of CPUs, or one if there is a single CPU.</para></listitem>
       </varlistentry>
 
-      <varlistentry id="opt.purge">
-        <term>
-          <mallctl>opt.purge</mallctl>
-          (<type>const char *</type>)
-          <literal>r-</literal>
-        </term>
-        <listitem><para>Purge mode is &ldquo;ratio&rdquo; (default) or
-        &ldquo;decay&rdquo;.  See <link
-        linkend="opt.lg_dirty_mult"><mallctl>opt.lg_dirty_mult</mallctl></link>
-        for details of the ratio mode.  See <link
-        linkend="opt.decay_time"><mallctl>opt.decay_time</mallctl></link> for
-        details of the decay mode.</para></listitem>
-      </varlistentry>
-
-      <varlistentry id="opt.lg_dirty_mult">
-        <term>
-          <mallctl>opt.lg_dirty_mult</mallctl>
-          (<type>ssize_t</type>)
-          <literal>r-</literal>
-        </term>
-        <listitem><para>Per-arena minimum ratio (log base 2) of active to dirty
-        pages.  Some dirty unused pages may be allowed to accumulate, within
-        the limit set by the ratio (or one chunk worth of dirty pages,
-        whichever is greater), before informing the kernel about some of those
-        pages via <citerefentry><refentrytitle>madvise</refentrytitle>
-        <manvolnum>2</manvolnum></citerefentry> or a similar system call.  This
-        provides the kernel with sufficient information to recycle dirty pages
-        if physical memory becomes scarce and the pages remain unused.  The
-        default minimum ratio is 8:1 (2^3:1); an option value of -1 will
-        disable dirty page purging.  See <link
-        linkend="arenas.lg_dirty_mult"><mallctl>arenas.lg_dirty_mult</mallctl></link>
-        and <link
-        linkend="arena.i.lg_dirty_mult"><mallctl>arena.&lt;i&gt;.lg_dirty_mult</mallctl></link>
-        for related dynamic control options.</para></listitem>
-      </varlistentry>
-
       <varlistentry id="opt.decay_time">
         <term>
           <mallctl>opt.decay_time</mallctl>
@@ -1518,20 +1482,6 @@
         settings.</para></listitem>
       </varlistentry>
 
-      <varlistentry id="arena.i.lg_dirty_mult">
-        <term>
-          <mallctl>arena.&lt;i&gt;.lg_dirty_mult</mallctl>
-          (<type>ssize_t</type>)
-          <literal>rw</literal>
-        </term>
-        <listitem><para>Current per-arena minimum ratio (log base 2) of active
-        to dirty pages for arena &lt;i&gt;.  Each time this interface is set and
-        the ratio is increased, pages are synchronously purged as necessary to
-        impose the new ratio.  See <link
-        linkend="opt.lg_dirty_mult"><mallctl>opt.lg_dirty_mult</mallctl></link>
-        for additional information.</para></listitem>
-      </varlistentry>
-
       <varlistentry id="arena.i.decay_time">
         <term>
           <mallctl>arena.&lt;i&gt;.decay_time</mallctl>
@@ -1778,20 +1728,6 @@
         initialized.</para></listitem>
       </varlistentry>
 
-      <varlistentry id="arenas.lg_dirty_mult">
-        <term>
-          <mallctl>arenas.lg_dirty_mult</mallctl>
-          (<type>ssize_t</type>)
-          <literal>rw</literal>
-        </term>
-        <listitem><para>Current default per-arena minimum ratio (log base 2) of
-        active to dirty pages, used to initialize <link
-        linkend="arena.i.lg_dirty_mult"><mallctl>arena.&lt;i&gt;.lg_dirty_mult</mallctl></link>
-        during arena creation.  See <link
-        linkend="opt.lg_dirty_mult"><mallctl>opt.lg_dirty_mult</mallctl></link>
-        for additional information.</para></listitem>
-      </varlistentry>
-
       <varlistentry id="arenas.decay_time">
         <term>
           <mallctl>arenas.decay_time</mallctl>
@@ -2137,18 +2073,6 @@
         </para></listitem>
       </varlistentry>
 
-      <varlistentry id="stats.arenas.i.lg_dirty_mult">
-        <term>
-          <mallctl>stats.arenas.&lt;i&gt;.lg_dirty_mult</mallctl>
-          (<type>ssize_t</type>)
-          <literal>r-</literal>
-        </term>
-        <listitem><para>Minimum ratio (log base 2) of active to dirty pages.
-        See <link
-        linkend="opt.lg_dirty_mult"><mallctl>opt.lg_dirty_mult</mallctl></link>
-        for details.</para></listitem>
-      </varlistentry>
-
       <varlistentry id="stats.arenas.i.decay_time">
         <term>
           <mallctl>stats.arenas.&lt;i&gt;.decay_time</mallctl>
diff --git a/include/jemalloc/internal/arena.h b/include/jemalloc/internal/arena.h
index 3bb1f19..4e20af4 100644
--- a/include/jemalloc/internal/arena.h
+++ b/include/jemalloc/internal/arena.h
@@ -7,23 +7,6 @@
 #define	LG_SLAB_MAXREGS		(LG_PAGE - LG_TINY_MIN)
 #define	SLAB_MAXREGS		(1U << LG_SLAB_MAXREGS)
 
-/*
- * The minimum ratio of active:dirty pages per arena is computed as:
- *
- *   (nactive >> lg_dirty_mult) >= ndirty
- *
- * So, supposing that lg_dirty_mult is 3, there can be no less than 8 times as
- * many active pages as dirty pages.
- */
-#define	LG_DIRTY_MULT_DEFAULT	3
-
-typedef enum {
-	purge_mode_ratio = 0,
-	purge_mode_decay = 1,
-
-	purge_mode_limit = 2
-} purge_mode_t;
-#define	PURGE_DEFAULT		purge_mode_ratio
 /* Default decay time in seconds. */
 #define	DECAY_TIME_DEFAULT	10
 /* Number of event ticks between time checks. */
@@ -203,9 +186,6 @@
 
 	dss_prec_t		dss_prec;
 
-	/* Minimum ratio (log base 2) of nactive:ndirty. */
-	ssize_t			lg_dirty_mult;
-
 	/* True if a thread is currently executing arena_purge_to_limit(). */
 	bool			purging;
 
@@ -274,9 +254,6 @@
 #endif
     ;
 
-extern purge_mode_t	opt_purge;
-extern const char	*purge_mode_names[];
-extern ssize_t		opt_lg_dirty_mult;
 extern ssize_t		opt_decay_time;
 
 extern const arena_bin_info_t	arena_bin_info[NBINS];
@@ -298,9 +275,6 @@
     extent_t *extent, size_t oldsize);
 void	arena_extent_ralloc_large_expand(tsdn_t *tsdn, arena_t *arena,
     extent_t *extent, size_t oldsize);
-ssize_t	arena_lg_dirty_mult_get(tsdn_t *tsdn, arena_t *arena);
-bool	arena_lg_dirty_mult_set(tsdn_t *tsdn, arena_t *arena,
-    ssize_t lg_dirty_mult);
 ssize_t	arena_decay_time_get(tsdn_t *tsdn, arena_t *arena);
 bool	arena_decay_time_set(tsdn_t *tsdn, arena_t *arena, ssize_t decay_time);
 void	arena_purge(tsdn_t *tsdn, arena_t *arena, bool all);
@@ -334,17 +308,15 @@
     size_t oldsize, size_t size, size_t alignment, bool zero, tcache_t *tcache);
 dss_prec_t	arena_dss_prec_get(tsdn_t *tsdn, arena_t *arena);
 bool	arena_dss_prec_set(tsdn_t *tsdn, arena_t *arena, dss_prec_t dss_prec);
-ssize_t	arena_lg_dirty_mult_default_get(void);
-bool	arena_lg_dirty_mult_default_set(ssize_t lg_dirty_mult);
 ssize_t	arena_decay_time_default_get(void);
 bool	arena_decay_time_default_set(ssize_t decay_time);
 void	arena_basic_stats_merge(tsdn_t *tsdn, arena_t *arena,
-    unsigned *nthreads, const char **dss, ssize_t *lg_dirty_mult,
-    ssize_t *decay_time, size_t *nactive, size_t *ndirty);
+    unsigned *nthreads, const char **dss, ssize_t *decay_time, size_t *nactive,
+    size_t *ndirty);
 void	arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
-    const char **dss, ssize_t *lg_dirty_mult, ssize_t *decay_time,
-    size_t *nactive, size_t *ndirty, arena_stats_t *astats,
-    malloc_bin_stats_t *bstats, malloc_large_stats_t *lstats);
+    const char **dss, ssize_t *decay_time, size_t *nactive, size_t *ndirty,
+    arena_stats_t *astats, malloc_bin_stats_t *bstats,
+    malloc_large_stats_t *lstats);
 unsigned	arena_nthreads_get(arena_t *arena, bool internal);
 void	arena_nthreads_inc(arena_t *arena, bool internal);
 void	arena_nthreads_dec(arena_t *arena, bool internal);
diff --git a/include/jemalloc/internal/ctl.h b/include/jemalloc/internal/ctl.h
index 3fbac20..4d4f304 100644
--- a/include/jemalloc/internal/ctl.h
+++ b/include/jemalloc/internal/ctl.h
@@ -35,7 +35,6 @@
 	bool			initialized;
 	unsigned		nthreads;
 	const char		*dss;
-	ssize_t			lg_dirty_mult;
 	ssize_t			decay_time;
 	size_t			pactive;
 	size_t			pdirty;
diff --git a/include/jemalloc/internal/private_symbols.txt b/include/jemalloc/internal/private_symbols.txt
index f9d6e9a..e52e7fe 100644
--- a/include/jemalloc/internal/private_symbols.txt
+++ b/include/jemalloc/internal/private_symbols.txt
@@ -34,10 +34,6 @@
 arena_get
 arena_ichoose
 arena_init
-arena_lg_dirty_mult_default_get
-arena_lg_dirty_mult_default_set
-arena_lg_dirty_mult_get
-arena_lg_dirty_mult_set
 arena_malloc
 arena_malloc_hard
 arena_maybe_purge
@@ -311,7 +307,6 @@
 opt_junk_alloc
 opt_junk_free
 opt_lg_chunk
-opt_lg_dirty_mult
 opt_lg_prof_interval
 opt_lg_prof_sample
 opt_lg_tcache_max
@@ -324,7 +319,6 @@
 opt_prof_leak
 opt_prof_prefix
 opt_prof_thread_active_init
-opt_purge
 opt_stats_print
 opt_tcache
 opt_utrace
@@ -399,7 +393,6 @@
 psz2ind_clamp
 psz2ind_impl
 psz2u
-purge_mode_names
 register_zone
 rtree_child_read
 rtree_child_read_hard
diff --git a/src/arena.c b/src/arena.c
index 2f0291e..3de0237 100644
--- a/src/arena.c
+++ b/src/arena.c
@@ -4,14 +4,6 @@
 /******************************************************************************/
 /* Data. */
 
-purge_mode_t	opt_purge = PURGE_DEFAULT;
-const char	*purge_mode_names[] = {
-	"ratio",
-	"decay",
-	"N/A"
-};
-ssize_t		opt_lg_dirty_mult = LG_DIRTY_MULT_DEFAULT;
-static ssize_t	lg_dirty_mult_default;
 ssize_t		opt_decay_time = DECAY_TIME_DEFAULT;
 static ssize_t	decay_time_default;
 
@@ -429,47 +421,10 @@
 	malloc_mutex_unlock(tsdn, &arena->lock);
 }
 
-static bool
-arena_lg_dirty_mult_valid(ssize_t lg_dirty_mult)
-{
-
-	return (lg_dirty_mult >= -1 && lg_dirty_mult < (ssize_t)(sizeof(size_t)
-	    << 3));
-}
-
-ssize_t
-arena_lg_dirty_mult_get(tsdn_t *tsdn, arena_t *arena)
-{
-	ssize_t lg_dirty_mult;
-
-	malloc_mutex_lock(tsdn, &arena->lock);
-	lg_dirty_mult = arena->lg_dirty_mult;
-	malloc_mutex_unlock(tsdn, &arena->lock);
-
-	return (lg_dirty_mult);
-}
-
-bool
-arena_lg_dirty_mult_set(tsdn_t *tsdn, arena_t *arena, ssize_t lg_dirty_mult)
-{
-
-	if (!arena_lg_dirty_mult_valid(lg_dirty_mult))
-		return (true);
-
-	malloc_mutex_lock(tsdn, &arena->lock);
-	arena->lg_dirty_mult = lg_dirty_mult;
-	arena_maybe_purge(tsdn, arena);
-	malloc_mutex_unlock(tsdn, &arena->lock);
-
-	return (false);
-}
-
 static void
 arena_decay_deadline_init(arena_t *arena)
 {
 
-	assert(opt_purge == purge_mode_decay);
-
 	/*
 	 * Generate a new deadline that is uniformly random within the next
 	 * epoch after the current one.
@@ -489,8 +444,6 @@
 arena_decay_deadline_reached(const arena_t *arena, const nstime_t *time)
 {
 
-	assert(opt_purge == purge_mode_decay);
-
 	return (nstime_compare(&arena->decay.deadline, time) <= 0);
 }
 
@@ -507,8 +460,6 @@
 	size_t npages_limit_backlog;
 	unsigned i;
 
-	assert(opt_purge == purge_mode_decay);
-
 	/*
 	 * For each element of decay_backlog, multiply by the corresponding
 	 * fixed-point smoothstep decay factor.  Sum the products, then divide
@@ -559,7 +510,6 @@
 	uint64_t nadvance_u64;
 	nstime_t delta;
 
-	assert(opt_purge == purge_mode_decay);
 	assert(arena_decay_deadline_reached(arena, time));
 
 	nstime_copy(&delta, time);
@@ -662,40 +612,10 @@
 }
 
 static void
-arena_maybe_purge_ratio(tsdn_t *tsdn, arena_t *arena)
-{
-
-	assert(opt_purge == purge_mode_ratio);
-
-	/* Don't purge if the option is disabled. */
-	if (arena->lg_dirty_mult < 0)
-		return;
-
-	/*
-	 * Iterate, since preventing recursive purging could otherwise leave too
-	 * many dirty pages.
-	 */
-	while (true) {
-		size_t threshold = (arena->nactive >> arena->lg_dirty_mult);
-		if (threshold < chunk_npages)
-			threshold = chunk_npages;
-		/*
-		 * Don't purge unless the number of purgeable pages exceeds the
-		 * threshold.
-		 */
-		if (arena->ndirty <= threshold)
-			return;
-		arena_purge_to_limit(tsdn, arena, threshold);
-	}
-}
-
-static void
-arena_maybe_purge_decay(tsdn_t *tsdn, arena_t *arena)
+arena_maybe_purge_helper(tsdn_t *tsdn, arena_t *arena)
 {
 	nstime_t time;
 
-	assert(opt_purge == purge_mode_decay);
-
 	/* Purge all or nothing if the option is disabled. */
 	if (arena->decay.time <= 0) {
 		if (arena->decay.time == 0)
@@ -743,10 +663,7 @@
 	if (arena->purging)
 		return;
 
-	if (opt_purge == purge_mode_ratio)
-		arena_maybe_purge_ratio(tsdn, arena);
-	else
-		arena_maybe_purge_decay(tsdn, arena);
+	arena_maybe_purge_helper(tsdn, arena);
 }
 
 static size_t
@@ -781,8 +698,7 @@
 		UNUSED extent_t *textent;
 
 		npages = extent_size_get(extent) >> LG_PAGE;
-		if (opt_purge == purge_mode_decay && arena->ndirty - (nstashed +
-		    npages) < ndirty_limit)
+		if (arena->ndirty - (nstashed + npages) < ndirty_limit)
 			break;
 
 		next = qr_next(extent, qr_link);
@@ -797,9 +713,6 @@
 		extent_ring_insert(purge_extents_sentinel, extent);
 
 		nstashed += npages;
-		if (opt_purge == purge_mode_ratio && arena->ndirty - nstashed <=
-		    ndirty_limit)
-			break;
 	}
 
 	malloc_mutex_unlock(tsdn, &arena->extents_mtx);
@@ -838,13 +751,8 @@
 }
 
 /*
- * NB: ndirty_limit is interpreted differently depending on opt_purge:
- *   - purge_mode_ratio: Purge as few dirty extents as possible to reach the
- *                       desired state:
- *                       (arena->ndirty <= ndirty_limit)
- *   - purge_mode_decay: Purge as many dirty extents as possible without
- *                       violating the invariant:
- *                       (arena->ndirty >= ndirty_limit)
+ *   ndirty_limit: Purge as many dirty extents as possible without violating the
+ *   invariant: (arena->ndirty >= ndirty_limit)
  */
 static void
 arena_purge_to_limit(tsdn_t *tsdn, arena_t *arena, size_t ndirty_limit)
@@ -863,9 +771,6 @@
 		size_t ndirty = arena_dirty_count(tsdn, arena);
 		assert(ndirty == arena->ndirty);
 	}
-	assert(opt_purge != purge_mode_ratio || (arena->nactive >>
-	    arena->lg_dirty_mult) < arena->ndirty || ndirty_limit == 0);
-
 	extent_init(&purge_extents_sentinel, arena, NULL, 0, 0, false, false,
 	    false, false);
 
@@ -1645,25 +1550,6 @@
 }
 
 ssize_t
-arena_lg_dirty_mult_default_get(void)
-{
-
-	return ((ssize_t)atomic_read_z((size_t *)&lg_dirty_mult_default));
-}
-
-bool
-arena_lg_dirty_mult_default_set(ssize_t lg_dirty_mult)
-{
-
-	if (opt_purge != purge_mode_ratio)
-		return (true);
-	if (!arena_lg_dirty_mult_valid(lg_dirty_mult))
-		return (true);
-	atomic_write_z((size_t *)&lg_dirty_mult_default, (size_t)lg_dirty_mult);
-	return (false);
-}
-
-ssize_t
 arena_decay_time_default_get(void)
 {
 
@@ -1674,8 +1560,6 @@
 arena_decay_time_default_set(ssize_t decay_time)
 {
 
-	if (opt_purge != purge_mode_decay)
-		return (true);
 	if (!arena_decay_time_valid(decay_time))
 		return (true);
 	atomic_write_z((size_t *)&decay_time_default, (size_t)decay_time);
@@ -1684,13 +1568,11 @@
 
 static void
 arena_basic_stats_merge_locked(arena_t *arena, unsigned *nthreads,
-    const char **dss, ssize_t *lg_dirty_mult, ssize_t *decay_time,
-    size_t *nactive, size_t *ndirty)
+    const char **dss, ssize_t *decay_time, size_t *nactive, size_t *ndirty)
 {
 
 	*nthreads += arena_nthreads_get(arena, false);
 	*dss = dss_prec_names[arena->dss_prec];
-	*lg_dirty_mult = arena->lg_dirty_mult;
 	*decay_time = arena->decay.time;
 	*nactive += arena->nactive;
 	*ndirty += arena->ndirty;
@@ -1698,29 +1580,28 @@
 
 void
 arena_basic_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
-    const char **dss, ssize_t *lg_dirty_mult, ssize_t *decay_time,
-    size_t *nactive, size_t *ndirty)
+    const char **dss, ssize_t *decay_time, size_t *nactive, size_t *ndirty)
 {
 
 	malloc_mutex_lock(tsdn, &arena->lock);
-	arena_basic_stats_merge_locked(arena, nthreads, dss, lg_dirty_mult,
-	    decay_time, nactive, ndirty);
+	arena_basic_stats_merge_locked(arena, nthreads, dss, decay_time,
+	    nactive, ndirty);
 	malloc_mutex_unlock(tsdn, &arena->lock);
 }
 
 void
 arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
-    const char **dss, ssize_t *lg_dirty_mult, ssize_t *decay_time,
-    size_t *nactive, size_t *ndirty, arena_stats_t *astats,
-    malloc_bin_stats_t *bstats, malloc_large_stats_t *lstats)
+    const char **dss, ssize_t *decay_time, size_t *nactive, size_t *ndirty,
+    arena_stats_t *astats, malloc_bin_stats_t *bstats,
+    malloc_large_stats_t *lstats)
 {
 	unsigned i;
 
 	cassert(config_stats);
 
 	malloc_mutex_lock(tsdn, &arena->lock);
-	arena_basic_stats_merge_locked(arena, nthreads, dss, lg_dirty_mult,
-	    decay_time, nactive, ndirty);
+	arena_basic_stats_merge_locked(arena, nthreads, dss, decay_time,
+	    nactive, ndirty);
 
 	astats->mapped += arena->stats.mapped;
 	astats->retained += arena->stats.retained;
@@ -1816,13 +1697,11 @@
 
 	arena->dss_prec = extent_dss_prec_get(tsdn);
 
-	arena->lg_dirty_mult = arena_lg_dirty_mult_default_get();
 	arena->purging = false;
 	arena->nactive = 0;
 	arena->ndirty = 0;
 
-	if (opt_purge == purge_mode_decay)
-		arena_decay_init(arena, arena_decay_time_default_get());
+	arena_decay_init(arena, arena_decay_time_default_get());
 
 	ql_new(&arena->large);
 	if (malloc_mutex_init(&arena->large_mtx, "arena_large",
@@ -1869,7 +1748,6 @@
 arena_boot(void)
 {
 
-	arena_lg_dirty_mult_default_set(opt_lg_dirty_mult);
 	arena_decay_time_default_set(opt_decay_time);
 }
 
diff --git a/src/ctl.c b/src/ctl.c
index 87fd8c7..b00991a 100644
--- a/src/ctl.c
+++ b/src/ctl.c
@@ -90,8 +90,6 @@
 CTL_PROTO(opt_dss)
 CTL_PROTO(opt_lg_chunk)
 CTL_PROTO(opt_narenas)
-CTL_PROTO(opt_purge)
-CTL_PROTO(opt_lg_dirty_mult)
 CTL_PROTO(opt_decay_time)
 CTL_PROTO(opt_stats_print)
 CTL_PROTO(opt_junk)
@@ -118,7 +116,6 @@
 CTL_PROTO(arena_i_decay)
 CTL_PROTO(arena_i_reset)
 CTL_PROTO(arena_i_dss)
-CTL_PROTO(arena_i_lg_dirty_mult)
 CTL_PROTO(arena_i_decay_time)
 CTL_PROTO(arena_i_extent_hooks)
 INDEX_PROTO(arena_i)
@@ -130,7 +127,6 @@
 INDEX_PROTO(arenas_lextent_i)
 CTL_PROTO(arenas_narenas)
 CTL_PROTO(arenas_initialized)
-CTL_PROTO(arenas_lg_dirty_mult)
 CTL_PROTO(arenas_decay_time)
 CTL_PROTO(arenas_quantum)
 CTL_PROTO(arenas_page)
@@ -171,7 +167,6 @@
 INDEX_PROTO(stats_arenas_i_lextents_j)
 CTL_PROTO(stats_arenas_i_nthreads)
 CTL_PROTO(stats_arenas_i_dss)
-CTL_PROTO(stats_arenas_i_lg_dirty_mult)
 CTL_PROTO(stats_arenas_i_decay_time)
 CTL_PROTO(stats_arenas_i_pactive)
 CTL_PROTO(stats_arenas_i_pdirty)
@@ -251,8 +246,6 @@
 	{NAME("dss"),		CTL(opt_dss)},
 	{NAME("lg_chunk"),	CTL(opt_lg_chunk)},
 	{NAME("narenas"),	CTL(opt_narenas)},
-	{NAME("purge"),		CTL(opt_purge)},
-	{NAME("lg_dirty_mult"),	CTL(opt_lg_dirty_mult)},
 	{NAME("decay_time"),	CTL(opt_decay_time)},
 	{NAME("stats_print"),	CTL(opt_stats_print)},
 	{NAME("junk"),		CTL(opt_junk)},
@@ -284,7 +277,6 @@
 	{NAME("decay"),		CTL(arena_i_decay)},
 	{NAME("reset"),		CTL(arena_i_reset)},
 	{NAME("dss"),		CTL(arena_i_dss)},
-	{NAME("lg_dirty_mult"),	CTL(arena_i_lg_dirty_mult)},
 	{NAME("decay_time"),	CTL(arena_i_decay_time)},
 	{NAME("extent_hooks"),	CTL(arena_i_extent_hooks)}
 };
@@ -323,7 +315,6 @@
 static const ctl_named_node_t arenas_node[] = {
 	{NAME("narenas"),	CTL(arenas_narenas)},
 	{NAME("initialized"),	CTL(arenas_initialized)},
-	{NAME("lg_dirty_mult"),	CTL(arenas_lg_dirty_mult)},
 	{NAME("decay_time"),	CTL(arenas_decay_time)},
 	{NAME("quantum"),	CTL(arenas_quantum)},
 	{NAME("page"),		CTL(arenas_page)},
@@ -396,7 +387,6 @@
 static const ctl_named_node_t stats_arenas_i_node[] = {
 	{NAME("nthreads"),	CTL(stats_arenas_i_nthreads)},
 	{NAME("dss"),		CTL(stats_arenas_i_dss)},
-	{NAME("lg_dirty_mult"),	CTL(stats_arenas_i_lg_dirty_mult)},
 	{NAME("decay_time"),	CTL(stats_arenas_i_decay_time)},
 	{NAME("pactive"),	CTL(stats_arenas_i_pactive)},
 	{NAME("pdirty"),	CTL(stats_arenas_i_pdirty)},
@@ -459,7 +449,6 @@
 
 	astats->nthreads = 0;
 	astats->dss = dss_prec_names[dss_prec_limit];
-	astats->lg_dirty_mult = -1;
 	astats->decay_time = -1;
 	astats->pactive = 0;
 	astats->pdirty = 0;
@@ -482,9 +471,8 @@
 
 	if (config_stats) {
 		arena_stats_merge(tsdn, arena, &cstats->nthreads, &cstats->dss,
-		    &cstats->lg_dirty_mult, &cstats->decay_time,
-		    &cstats->pactive, &cstats->pdirty, &cstats->astats,
-		    cstats->bstats, cstats->lstats);
+		    &cstats->decay_time, &cstats->pactive, &cstats->pdirty,
+		    &cstats->astats, cstats->bstats, cstats->lstats);
 
 		for (i = 0; i < NBINS; i++) {
 			cstats->allocated_small += cstats->bstats[i].curregs *
@@ -495,8 +483,8 @@
 		}
 	} else {
 		arena_basic_stats_merge(tsdn, arena, &cstats->nthreads,
-		    &cstats->dss, &cstats->lg_dirty_mult, &cstats->decay_time,
-		    &cstats->pactive, &cstats->pdirty);
+		    &cstats->dss, &cstats->decay_time, &cstats->pactive,
+		    &cstats->pdirty);
 	}
 }
 
@@ -1150,8 +1138,6 @@
 CTL_RO_NL_GEN(opt_dss, opt_dss, const char *)
 CTL_RO_NL_GEN(opt_lg_chunk, opt_lg_chunk, size_t)
 CTL_RO_NL_GEN(opt_narenas, opt_narenas, unsigned)
-CTL_RO_NL_GEN(opt_purge, purge_mode_names[opt_purge], const char *)
-CTL_RO_NL_GEN(opt_lg_dirty_mult, opt_lg_dirty_mult, ssize_t)
 CTL_RO_NL_GEN(opt_decay_time, opt_decay_time, ssize_t)
 CTL_RO_NL_GEN(opt_stats_print, opt_stats_print, bool)
 CTL_RO_NL_CGEN(config_fill, opt_junk, opt_junk, const char *)
@@ -1563,41 +1549,6 @@
 }
 
 static int
-arena_i_lg_dirty_mult_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
-    void *oldp, size_t *oldlenp, void *newp, size_t newlen)
-{
-	int ret;
-	unsigned arena_ind = (unsigned)mib[1];
-	arena_t *arena;
-
-	arena = arena_get(tsd_tsdn(tsd), arena_ind, false);
-	if (arena == NULL) {
-		ret = EFAULT;
-		goto label_return;
-	}
-
-	if (oldp != NULL && oldlenp != NULL) {
-		size_t oldval = arena_lg_dirty_mult_get(tsd_tsdn(tsd), arena);
-		READ(oldval, ssize_t);
-	}
-	if (newp != NULL) {
-		if (newlen != sizeof(ssize_t)) {
-			ret = EINVAL;
-			goto label_return;
-		}
-		if (arena_lg_dirty_mult_set(tsd_tsdn(tsd), arena,
-		    *(ssize_t *)newp)) {
-			ret = EFAULT;
-			goto label_return;
-		}
-	}
-
-	ret = 0;
-label_return:
-	return (ret);
-}
-
-static int
 arena_i_decay_time_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
     size_t *oldlenp, void *newp, size_t newlen)
 {
@@ -1734,32 +1685,6 @@
 }
 
 static int
-arenas_lg_dirty_mult_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
-    void *oldp, size_t *oldlenp, void *newp, size_t newlen)
-{
-	int ret;
-
-	if (oldp != NULL && oldlenp != NULL) {
-		size_t oldval = arena_lg_dirty_mult_default_get();
-		READ(oldval, ssize_t);
-	}
-	if (newp != NULL) {
-		if (newlen != sizeof(ssize_t)) {
-			ret = EINVAL;
-			goto label_return;
-		}
-		if (arena_lg_dirty_mult_default_set(*(ssize_t *)newp)) {
-			ret = EFAULT;
-			goto label_return;
-		}
-	}
-
-	ret = 0;
-label_return:
-	return (ret);
-}
-
-static int
 arenas_decay_time_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
     size_t *oldlenp, void *newp, size_t newlen)
 {
@@ -1972,8 +1897,6 @@
 CTL_RO_CGEN(config_stats, stats_retained, ctl_stats.retained, size_t)
 
 CTL_RO_GEN(stats_arenas_i_dss, ctl_stats.arenas[mib[2]].dss, const char *)
-CTL_RO_GEN(stats_arenas_i_lg_dirty_mult, ctl_stats.arenas[mib[2]].lg_dirty_mult,
-    ssize_t)
 CTL_RO_GEN(stats_arenas_i_decay_time, ctl_stats.arenas[mib[2]].decay_time,
     ssize_t)
 CTL_RO_GEN(stats_arenas_i_nthreads, ctl_stats.arenas[mib[2]].nthreads, unsigned)
diff --git a/src/jemalloc.c b/src/jemalloc.c
index 2415855..580b23f 100644
--- a/src/jemalloc.c
+++ b/src/jemalloc.c
@@ -1053,25 +1053,6 @@
 			}
 			CONF_HANDLE_UNSIGNED(opt_narenas, "narenas", 1,
 			    UINT_MAX, false)
-			if (strncmp("purge", k, klen) == 0) {
-				int i;
-				bool match = false;
-				for (i = 0; i < purge_mode_limit; i++) {
-					if (strncmp(purge_mode_names[i], v,
-					    vlen) == 0) {
-						opt_purge = (purge_mode_t)i;
-						match = true;
-						break;
-					}
-				}
-				if (!match) {
-					malloc_conf_error("Invalid conf value",
-					    k, klen, v, vlen);
-				}
-				continue;
-			}
-			CONF_HANDLE_SSIZE_T(opt_lg_dirty_mult, "lg_dirty_mult",
-			    -1, (sizeof(size_t) << 3) - 1)
 			CONF_HANDLE_SSIZE_T(opt_decay_time, "decay_time", -1,
 			    NSTIME_SEC_MAX);
 			CONF_HANDLE_BOOL(opt_stats_print, "stats_print", true)
diff --git a/src/stats.c b/src/stats.c
index d881585..185ccac 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -210,7 +210,7 @@
 {
 	unsigned nthreads;
 	const char *dss;
-	ssize_t lg_dirty_mult, decay_time;
+	ssize_t decay_time;
 	size_t page, pactive, pdirty, mapped, retained, metadata;
 	uint64_t npurge, nmadvise, purged;
 	size_t small_allocated;
@@ -226,25 +226,12 @@
 	CTL_M2_GET("stats.arenas.0.dss", i, &dss, const char *);
 	malloc_cprintf(write_cb, cbopaque, "dss allocation precedence: %s\n",
 	    dss);
-	CTL_M2_GET("stats.arenas.0.lg_dirty_mult", i, &lg_dirty_mult, ssize_t);
-	if (opt_purge == purge_mode_ratio) {
-		if (lg_dirty_mult >= 0) {
-			malloc_cprintf(write_cb, cbopaque,
-			    "min active:dirty page ratio: %u:1\n",
-			    (1U << lg_dirty_mult));
-		} else {
-			malloc_cprintf(write_cb, cbopaque,
-			    "min active:dirty page ratio: N/A\n");
-		}
-	}
 	CTL_M2_GET("stats.arenas.0.decay_time", i, &decay_time, ssize_t);
-	if (opt_purge == purge_mode_decay) {
-		if (decay_time >= 0) {
-			malloc_cprintf(write_cb, cbopaque, "decay time: %zd\n",
-			    decay_time);
-		} else
-			malloc_cprintf(write_cb, cbopaque, "decay time: N/A\n");
-	}
+	if (decay_time >= 0) {
+		malloc_cprintf(write_cb, cbopaque, "decay time: %zd\n",
+		    decay_time);
+	} else
+		malloc_cprintf(write_cb, cbopaque, "decay time: N/A\n");
 	CTL_M2_GET("stats.arenas.0.pactive", i, &pactive, size_t);
 	CTL_M2_GET("stats.arenas.0.pdirty", i, &pdirty, size_t);
 	CTL_M2_GET("stats.arenas.0.npurge", i, &npurge, uint64_t);
@@ -433,12 +420,7 @@
 		OPT_WRITE_CHAR_P(dss)
 		OPT_WRITE_UNSIGNED(narenas)
 		OPT_WRITE_CHAR_P(purge)
-		if (opt_purge == purge_mode_ratio) {
-			OPT_WRITE_SSIZE_T_MUTABLE(lg_dirty_mult,
-			    arenas.lg_dirty_mult)
-		}
-		if (opt_purge == purge_mode_decay)
-			OPT_WRITE_SSIZE_T_MUTABLE(decay_time, arenas.decay_time)
+		OPT_WRITE_SSIZE_T_MUTABLE(decay_time, arenas.decay_time)
 		OPT_WRITE_BOOL(stats_print)
 		OPT_WRITE_CHAR_P(junk)
 		OPT_WRITE_BOOL(zero)
@@ -479,24 +461,10 @@
 		CTL_GET("arenas.page", &sv, size_t);
 		malloc_cprintf(write_cb, cbopaque, "Page size: %zu\n", sv);
 
-		CTL_GET("arenas.lg_dirty_mult", &ssv, ssize_t);
-		if (opt_purge == purge_mode_ratio) {
-			if (ssv >= 0) {
-				malloc_cprintf(write_cb, cbopaque,
-				    "Min active:dirty page ratio per arena: "
-				    "%u:1\n", (1U << ssv));
-			} else {
-				malloc_cprintf(write_cb, cbopaque,
-				    "Min active:dirty page ratio per arena: "
-				    "N/A\n");
-			}
-		}
 		CTL_GET("arenas.decay_time", &ssv, ssize_t);
-		if (opt_purge == purge_mode_decay) {
-			malloc_cprintf(write_cb, cbopaque,
-			    "Unused dirty page decay time: %zd%s\n",
-			    ssv, (ssv < 0) ? " (no decay)" : "");
-		}
+		malloc_cprintf(write_cb, cbopaque,
+		    "Unused dirty page decay time: %zd%s\n", ssv, (ssv < 0) ?
+		    " (no decay)" : "");
 		if (je_mallctl("arenas.tcache_max", &sv, &ssz, NULL, 0) == 0) {
 			malloc_cprintf(write_cb, cbopaque,
 			    "Maximum thread-cached size class: %zu\n", sv);
diff --git a/test/unit/decay.c b/test/unit/decay.c
index 333a722..058a58c 100644
--- a/test/unit/decay.c
+++ b/test/unit/decay.c
@@ -1,6 +1,6 @@
 #include "test/jemalloc_test.h"
 
-const char *malloc_conf = "purge:decay,decay_time:1,lg_tcache_max:0";
+const char *malloc_conf = "decay_time:1,lg_tcache_max:0";
 
 static nstime_monotonic_t *nstime_monotonic_orig;
 static nstime_update_t *nstime_update_orig;
@@ -33,8 +33,6 @@
 	size_t sz, large0;
 	void *p;
 
-	test_skip_if(opt_purge != purge_mode_decay);
-
 	decay_ticker = decay_ticker_get(tsd_fetch(), 0);
 	assert_ptr_not_null(decay_ticker,
 	    "Unexpected failure getting decay ticker");
@@ -213,8 +211,6 @@
 	unsigned i, nupdates0;
 	nstime_t time, decay_time, deadline;
 
-	test_skip_if(opt_purge != purge_mode_decay);
-
 	/*
 	 * Allocate a bunch of large objects, pause the clock, deallocate the
 	 * objects, restore the clock, then [md]allocx() in a tight loop to
@@ -307,8 +303,6 @@
 	size_t sz, large0;
 	unsigned i, nupdates0;
 
-	test_skip_if(opt_purge != purge_mode_decay);
-
 	sz = sizeof(size_t);
 	assert_d_eq(mallctl("arenas.lextent.0.size", &large0, &sz, NULL, 0), 0,
 	    "Unexpected mallctl failure");
diff --git a/test/unit/mallctl.c b/test/unit/mallctl.c
index 1954bfc..ee57dd5 100644
--- a/test/unit/mallctl.c
+++ b/test/unit/mallctl.c
@@ -163,8 +163,6 @@
 	TEST_MALLCTL_OPT(size_t, lg_chunk, always);
 	TEST_MALLCTL_OPT(const char *, dss, always);
 	TEST_MALLCTL_OPT(unsigned, narenas, always);
-	TEST_MALLCTL_OPT(const char *, purge, always);
-	TEST_MALLCTL_OPT(ssize_t, lg_dirty_mult, always);
 	TEST_MALLCTL_OPT(ssize_t, decay_time, always);
 	TEST_MALLCTL_OPT(bool, stats_print, always);
 	TEST_MALLCTL_OPT(const char *, junk, fill);
@@ -349,47 +347,11 @@
 }
 TEST_END
 
-TEST_BEGIN(test_arena_i_lg_dirty_mult)
-{
-	ssize_t lg_dirty_mult, orig_lg_dirty_mult, prev_lg_dirty_mult;
-	size_t sz = sizeof(ssize_t);
-
-	test_skip_if(opt_purge != purge_mode_ratio);
-
-	assert_d_eq(mallctl("arena.0.lg_dirty_mult", &orig_lg_dirty_mult, &sz,
-	    NULL, 0), 0, "Unexpected mallctl() failure");
-
-	lg_dirty_mult = -2;
-	assert_d_eq(mallctl("arena.0.lg_dirty_mult", NULL, NULL,
-	    &lg_dirty_mult, sizeof(ssize_t)), EFAULT,
-	    "Unexpected mallctl() success");
-
-	lg_dirty_mult = (sizeof(size_t) << 3);
-	assert_d_eq(mallctl("arena.0.lg_dirty_mult", NULL, NULL,
-	    &lg_dirty_mult, sizeof(ssize_t)), EFAULT,
-	    "Unexpected mallctl() success");
-
-	for (prev_lg_dirty_mult = orig_lg_dirty_mult, lg_dirty_mult = -1;
-	    lg_dirty_mult < (ssize_t)(sizeof(size_t) << 3); prev_lg_dirty_mult
-	    = lg_dirty_mult, lg_dirty_mult++) {
-		ssize_t old_lg_dirty_mult;
-
-		assert_d_eq(mallctl("arena.0.lg_dirty_mult", &old_lg_dirty_mult,
-		    &sz, &lg_dirty_mult, sizeof(ssize_t)), 0,
-		    "Unexpected mallctl() failure");
-		assert_zd_eq(old_lg_dirty_mult, prev_lg_dirty_mult,
-		    "Unexpected old arena.0.lg_dirty_mult");
-	}
-}
-TEST_END
-
 TEST_BEGIN(test_arena_i_decay_time)
 {
 	ssize_t decay_time, orig_decay_time, prev_decay_time;
 	size_t sz = sizeof(ssize_t);
 
-	test_skip_if(opt_purge != purge_mode_decay);
-
 	assert_d_eq(mallctl("arena.0.decay_time", &orig_decay_time, &sz,
 	    NULL, 0), 0, "Unexpected mallctl() failure");
 
@@ -515,47 +477,11 @@
 }
 TEST_END
 
-TEST_BEGIN(test_arenas_lg_dirty_mult)
-{
-	ssize_t lg_dirty_mult, orig_lg_dirty_mult, prev_lg_dirty_mult;
-	size_t sz = sizeof(ssize_t);
-
-	test_skip_if(opt_purge != purge_mode_ratio);
-
-	assert_d_eq(mallctl("arenas.lg_dirty_mult", &orig_lg_dirty_mult, &sz,
-	    NULL, 0), 0, "Unexpected mallctl() failure");
-
-	lg_dirty_mult = -2;
-	assert_d_eq(mallctl("arenas.lg_dirty_mult", NULL, NULL,
-	    &lg_dirty_mult, sizeof(ssize_t)), EFAULT,
-	    "Unexpected mallctl() success");
-
-	lg_dirty_mult = (sizeof(size_t) << 3);
-	assert_d_eq(mallctl("arenas.lg_dirty_mult", NULL, NULL,
-	    &lg_dirty_mult, sizeof(ssize_t)), EFAULT,
-	    "Unexpected mallctl() success");
-
-	for (prev_lg_dirty_mult = orig_lg_dirty_mult, lg_dirty_mult = -1;
-	    lg_dirty_mult < (ssize_t)(sizeof(size_t) << 3); prev_lg_dirty_mult =
-	    lg_dirty_mult, lg_dirty_mult++) {
-		ssize_t old_lg_dirty_mult;
-
-		assert_d_eq(mallctl("arenas.lg_dirty_mult", &old_lg_dirty_mult,
-		    &sz, &lg_dirty_mult, sizeof(ssize_t)), 0,
-		    "Unexpected mallctl() failure");
-		assert_zd_eq(old_lg_dirty_mult, prev_lg_dirty_mult,
-		    "Unexpected old arenas.lg_dirty_mult");
-	}
-}
-TEST_END
-
 TEST_BEGIN(test_arenas_decay_time)
 {
 	ssize_t decay_time, orig_decay_time, prev_decay_time;
 	size_t sz = sizeof(ssize_t);
 
-	test_skip_if(opt_purge != purge_mode_decay);
-
 	assert_d_eq(mallctl("arenas.decay_time", &orig_decay_time, &sz,
 	    NULL, 0), 0, "Unexpected mallctl() failure");
 
@@ -669,7 +595,6 @@
 
 	TEST_STATS_ARENAS(unsigned, nthreads);
 	TEST_STATS_ARENAS(const char *, dss);
-	TEST_STATS_ARENAS(ssize_t, lg_dirty_mult);
 	TEST_STATS_ARENAS(ssize_t, decay_time);
 	TEST_STATS_ARENAS(size_t, pactive);
 	TEST_STATS_ARENAS(size_t, pdirty);
@@ -694,13 +619,11 @@
 	    test_tcache_none,
 	    test_tcache,
 	    test_thread_arena,
-	    test_arena_i_lg_dirty_mult,
 	    test_arena_i_decay_time,
 	    test_arena_i_purge,
 	    test_arena_i_decay,
 	    test_arena_i_dss,
 	    test_arenas_initialized,
-	    test_arenas_lg_dirty_mult,
 	    test_arenas_decay_time,
 	    test_arenas_constants,
 	    test_arenas_bin_constants,