diff --git a/src/hotspot/share/memory/arena.hpp b/src/hotspot/share/memory/arena.hpp
index 4c95d656e25..ed441eca851 100644
--- a/src/hotspot/share/memory/arena.hpp
+++ b/src/hotspot/share/memory/arena.hpp
@@ -87,7 +87,7 @@ public:
 class Arena : public CHeapObjBase {
 public:
 
-  enum class Tag {
+  enum class Tag : uint8_t {
     tag_other = 0,
     tag_ra,   // resource area
     tag_ha,   // handle area
@@ -101,6 +101,7 @@ protected:
 
   MEMFLAGS    _flags;           // Memory tracking flags
   const Tag _tag;
+  uint32_t _init_size;
   Chunk* _first;                // First chunk
   Chunk* _chunk;                // current chunk
   char* _hwm;                   // High water mark
diff --git a/src/hotspot/share/nmt/memReporter.cpp b/src/hotspot/share/nmt/memReporter.cpp
index 1dc4e58169d..e5969bbaf8f 100644
--- a/src/hotspot/share/nmt/memReporter.cpp
+++ b/src/hotspot/share/nmt/memReporter.cpp
@@ -31,7 +31,14 @@
 #include "nmt/memoryFileTracker.hpp"
 #include "nmt/threadStackTracker.hpp"
 #include "nmt/virtualMemoryTracker.hpp"
+#include "utilities/debug.hpp"
 #include "utilities/globalDefinitions.hpp"
+#include "utilities/ostream.hpp"
+
+#define INDENT_BY(num_chars, CODE) { \
+  streamIndentor si(out, num_chars); \
+  { CODE }                           \
+}
 
 // Diff two counters, express them as signed, with range checks
 static ssize_t counter_diff(size_t c1, size_t c2) {
@@ -43,6 +50,15 @@ static ssize_t counter_diff(size_t c1, size_t c2) {
   return c1 - c2;
 }
 
+MemReporterBase::MemReporterBase(outputStream* out, size_t scale) :
+  _scale(scale), _output(out) {
+  _output->set_autoindent(true);
+}
+
+MemReporterBase::~MemReporterBase() {
+  _output->set_autoindent(false);
+}
+
 size_t MemReporterBase::reserved_total(const MallocMemory* malloc, const VirtualMemory* vm) {
   return malloc->malloc_size() + malloc->arena_size() + vm->reserved();
 }
@@ -105,27 +121,15 @@ void MemReporterBase::print_virtual_memory(size_t reserved, size_t committed, si
   }
 }
 
-void MemReporterBase::print_malloc_line(const MemoryCounter* c) const {
-  output()->print("%28s", " ");
-  print_malloc(c);
-  output()->print_cr(" ");
-}
-
-void MemReporterBase::print_virtual_memory_line(size_t reserved, size_t committed, size_t peak) const {
-  output()->print("%28s", " ");
-  print_virtual_memory(reserved, committed, peak);
-  output()->print_cr(" ");
-}
-
-void MemReporterBase::print_arena_line(const MemoryCounter* c) const {
+void MemReporterBase::print_arena(const MemoryCounter* c) const {
   const char* scale = current_scale();
   outputStream* out = output();
 
   const size_t amount = c->size();
   const size_t count = c->count();
 
-  out->print("%27s (arena=" SIZE_FORMAT "%s #" SIZE_FORMAT ")", "",
-    amount_in_current_scale(amount), scale, count);
+  out->print("(arena=" SIZE_FORMAT "%s #" SIZE_FORMAT ")",
+             amount_in_current_scale(amount), scale, count);
 
   size_t pk_amount = c->peak_size();
   if (pk_amount == amount) {
@@ -135,8 +139,6 @@ void MemReporterBase::print_arena_line(const MemoryCounter* c) const {
     out->print(" (peak=" SIZE_FORMAT "%s #" SIZE_FORMAT ")",
         amount_in_current_scale(pk_amount), scale, pk_count);
   }
-
-  out->cr();
 }
 
 void MemReporterBase::print_virtual_memory_region(const char* type, address base, size_t size) const {
@@ -156,7 +158,9 @@ void MemSummaryReporter::report() {
   size_t total_committed_amount = total_malloced_bytes + total_mmap_committed_bytes;
 
   // Overall total
-  out->print_cr("\nNative Memory Tracking:\n");
+  out->cr();
+  out->print_cr("Native Memory Tracking:");
+  out->cr();
 
   if (scale() > 1) {
     out->print_cr("(Omitting categories weighting less than 1%s)", current_scale());
@@ -166,13 +170,15 @@ void MemSummaryReporter::report() {
   out->print("Total: ");
   print_total(total_reserved_amount, total_committed_amount);
   out->cr();
-  out->print_cr("       malloc: " SIZE_FORMAT "%s #" SIZE_FORMAT ", peak=" SIZE_FORMAT "%s #" SIZE_FORMAT,
-                amount_in_current_scale(total_malloced_bytes), current_scale(),
-                _malloc_snapshot->total_count(),
-                amount_in_current_scale(_malloc_snapshot->total_peak()),
-                current_scale(), _malloc_snapshot->total_peak_count());
-  out->print("       mmap:   ");
-  print_total(total_mmap_reserved_bytes, total_mmap_committed_bytes);
+  INDENT_BY(7,
+    out->print_cr("malloc: " SIZE_FORMAT "%s #" SIZE_FORMAT ", peak=" SIZE_FORMAT "%s #" SIZE_FORMAT,
+                  amount_in_current_scale(total_malloced_bytes), current_scale(),
+                  _malloc_snapshot->total_count(),
+                  amount_in_current_scale(_malloc_snapshot->total_peak()),
+                  current_scale(), _malloc_snapshot->total_peak_count());
+    out->print("mmap:   ");
+    print_total(total_mmap_reserved_bytes, total_mmap_committed_bytes);
+  )
   out->cr();
   out->cr();
 
@@ -218,7 +224,8 @@ void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag,
 
   outputStream* out   = output();
   const char*   scale = current_scale();
-  out->print("-%26s (", NMTUtil::flag_to_name(flag));
+  constexpr int indent = 28;
+  out->print("-%*s (", indent - 2, NMTUtil::flag_to_name(flag));
   print_total(reserved_amount, committed_amount);
 #if INCLUDE_CDS
   if (flag == mtClassShared) {
@@ -229,39 +236,43 @@ void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag,
 #endif
   out->print_cr(")");
 
+  streamIndentor si(out, indent);
+
   if (flag == mtClass) {
     // report class count
-    out->print_cr("%27s (classes #" SIZE_FORMAT ")",
-      " ", (_instance_class_count + _array_class_count));
-    out->print_cr("%27s (  instance classes #" SIZE_FORMAT ", array classes #" SIZE_FORMAT ")",
-      " ", _instance_class_count, _array_class_count);
+    out->print_cr("(classes #" SIZE_FORMAT ")", (_instance_class_count + _array_class_count));
+    out->print_cr("(  instance classes #" SIZE_FORMAT ", array classes #" SIZE_FORMAT ")",
+                  _instance_class_count, _array_class_count);
   } else if (flag == mtThread) {
     const VirtualMemory* thread_stack_usage =
      _vm_snapshot->by_type(mtThreadStack);
     // report thread count
-    out->print_cr("%27s (threads #" SIZE_FORMAT ")", " ", ThreadStackTracker::thread_count());
-    out->print("%27s (stack: ", " ");
+    out->print_cr("(threads #" SIZE_FORMAT ")", ThreadStackTracker::thread_count());
+    out->print("(stack: ");
     print_total(thread_stack_usage->reserved(), thread_stack_usage->committed(), thread_stack_usage->peak_size());
     out->print_cr(")");
   }
 
    // report malloc'd memory
   if (amount_in_current_scale(MAX2(malloc_memory->malloc_size(), pk_malloc)) > 0) {
-    print_malloc_line(malloc_memory->malloc_counter());
+    print_malloc(malloc_memory->malloc_counter());
+    out->cr();
   }
 
   if (amount_in_current_scale(MAX2(virtual_memory->reserved(), pk_vm)) > 0) {
-    print_virtual_memory_line(virtual_memory->reserved(), virtual_memory->committed(), virtual_memory->peak_size());
+    print_virtual_memory(virtual_memory->reserved(), virtual_memory->committed(), virtual_memory->peak_size());
+    out->cr();
   }
 
   if (amount_in_current_scale(MAX2(malloc_memory->arena_size(), pk_arena)) > 0) {
-    print_arena_line(malloc_memory->arena_counter());
+    print_arena(malloc_memory->arena_counter());
+    out->cr();
   }
 
   if (flag == mtNMT &&
     amount_in_current_scale(_malloc_snapshot->malloc_overhead()) > 0) {
-    out->print_cr("%27s (tracking overhead=" SIZE_FORMAT "%s)", " ",
-      amount_in_current_scale(_malloc_snapshot->malloc_overhead()), scale);
+    out->print_cr("(tracking overhead=" SIZE_FORMAT "%s)",
+                   amount_in_current_scale(_malloc_snapshot->malloc_overhead()), scale);
   } else if (flag == mtClass) {
     // Metadata information
     report_metadata(Metaspace::NonClassType);
@@ -269,7 +280,7 @@ void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag,
       report_metadata(Metaspace::ClassType);
     }
   }
-  out->print_cr(" ");
+  out->cr();
 }
 
 void MemSummaryReporter::report_metadata(Metaspace::MetadataType type) const {
@@ -292,13 +303,13 @@ void MemSummaryReporter::report_metadata(Metaspace::MetadataType type) const {
   size_t waste = stats.committed() - stats.used();
   float waste_percentage = stats.committed() > 0 ? (((float)waste * 100)/(float)stats.committed()) : 0.0f;
 
-  out->print_cr("%27s (  %s)", " ", name);
-  out->print("%27s (    ", " ");
+  out->print_cr("(  %s)", name);
+  out->print("(    ");
   print_total(stats.reserved(), stats.committed());
   out->print_cr(")");
-  out->print_cr("%27s (    used=" SIZE_FORMAT "%s)", " ", amount_in_current_scale(stats.used()), scale);
-  out->print_cr("%27s (    waste=" SIZE_FORMAT "%s =%2.2f%%)", " ", amount_in_current_scale(waste),
-    scale, waste_percentage);
+  out->print_cr("(    used=" SIZE_FORMAT "%s)", amount_in_current_scale(stats.used()), scale);
+  out->print_cr("(    waste=" SIZE_FORMAT "%s =%2.2f%%)", amount_in_current_scale(waste),
+                scale, waste_percentage);
 }
 
 void MemDetailReporter::report_detail() {
@@ -333,12 +344,15 @@ int MemDetailReporter::report_malloc_sites() {
     }
     const NativeCallStack* stack = malloc_site->call_stack();
     stack->print_on(out);
-    out->print("%29s", " ");
     MEMFLAGS flag = malloc_site->flag();
     assert(NMTUtil::flag_is_valid(flag) && flag != mtNone,
       "Must have a valid memory type");
-    print_malloc(malloc_site->counter(), flag);
-    out->print_cr("\n");
+    INDENT_BY(29,
+      out->print("(");
+      print_malloc(malloc_site->counter(), flag);
+      out->print_cr(")");
+    )
+    out->cr();
   }
   return num_omitted;
 }
@@ -350,6 +364,7 @@ int MemDetailReporter::report_virtual_memory_allocation_sites()  {
   if (virtual_memory_itr.is_empty()) return 0;
 
   outputStream* out = output();
+
   const VirtualMemoryAllocationSite*  virtual_memory_site;
   int num_omitted = 0;
   while ((virtual_memory_site = virtual_memory_itr.next()) != nullptr) {
@@ -366,13 +381,16 @@ int MemDetailReporter::report_virtual_memory_allocation_sites()  {
     }
     const NativeCallStack* stack = virtual_memory_site->call_stack();
     stack->print_on(out);
-    out->print("%28s (", " ");
-    print_total(virtual_memory_site->reserved(), virtual_memory_site->committed());
-    MEMFLAGS flag = virtual_memory_site->flag();
-    if (flag != mtNone) {
-      out->print(" Type=%s", NMTUtil::flag_to_name(flag));
-    }
-    out->print_cr(")\n");
+    INDENT_BY(29,
+      out->print("(");
+      print_total(virtual_memory_site->reserved(), virtual_memory_site->committed());
+      const MEMFLAGS flag = virtual_memory_site->flag();
+      if (flag != mtNone) {
+        out->print(" Type=%s", NMTUtil::flag_to_name(flag));
+      }
+      out->print_cr(")");
+    )
+    out->cr();
   }
   return num_omitted;
 }
@@ -409,14 +427,14 @@ void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion*
   const NativeCallStack*  stack = reserved_rgn->call_stack();
   bool all_committed = reserved_rgn->size() == reserved_rgn->committed_size();
   const char* region_type = (all_committed ? "reserved and committed" : "reserved");
-  out->print_cr(" ");
+  out->cr();
   print_virtual_memory_region(region_type, reserved_rgn->base(), reserved_rgn->size());
   out->print(" for %s", NMTUtil::flag_to_name(reserved_rgn->flag()));
   if (stack->is_empty()) {
-    out->print_cr(" ");
+    out->cr();
   } else {
     out->print_cr(" from");
-    stack->print_on(out, 4);
+    INDENT_BY(4, stack->print_on(out);)
   }
 
   if (all_committed) {
@@ -437,20 +455,33 @@ void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion*
     // Don't report if size is too small
     if (amount_in_current_scale(committed_rgn->size()) == 0) continue;
     stack = committed_rgn->call_stack();
-    out->print("\n\t");
-    print_virtual_memory_region("committed", committed_rgn->base(), committed_rgn->size());
-    if (stack->is_empty()) {
-      out->print_cr(" ");
-    } else {
-      out->print_cr(" from");
-      stack->print_on(out, 12);
-    }
+    out->cr();
+    INDENT_BY(8,
+      print_virtual_memory_region("committed", committed_rgn->base(), committed_rgn->size());
+      if (stack->is_empty()) {
+        out->cr();
+      } else {
+        out->print_cr(" from");
+        INDENT_BY(4, stack->print_on(out);)
+      }
+    )
   }
 }
 
+void MemDetailReporter::report_memory_file_allocations() {
+  stringStream st;
+  {
+    MemoryFileTracker::Instance::Locker lock;
+    MemoryFileTracker::Instance::print_all_reports_on(&st, scale());
+  }
+  output()->print_raw(st.freeze());
+}
+
 void MemSummaryDiffReporter::report_diff() {
   outputStream* out = output();
-  out->print_cr("\nNative Memory Tracking:\n");
+  out->cr();
+  out->print_cr("Native Memory Tracking:");
+  out->cr();
 
   if (scale() > 1) {
     out->print_cr("(Omitting categories weighting less than 1%s)", current_scale());
@@ -463,7 +494,8 @@ void MemSummaryDiffReporter::report_diff() {
     _current_baseline.total_committed_memory(), _early_baseline.total_reserved_memory(),
     _early_baseline.total_committed_memory());
 
-  out->print_cr("\n");
+  out->cr();
+  out->cr();
 
   // Summary diff by memory type
   for (int index = 0; index < mt_number_of_types; index ++) {
@@ -548,6 +580,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
 
   outputStream* out = output();
   const char* scale = current_scale();
+  constexpr int indent = 28;
 
   // Total reserved and committed memory in current baseline
   size_t current_reserved_amount  = reserved_total (current_malloc, current_vm);
@@ -581,15 +614,17 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
       diff_in_current_scale(current_reserved_amount, early_reserved_amount) != 0) {
 
     // print summary line
-    out->print("-%26s (", NMTUtil::flag_to_name(flag));
+    out->print("-%*s (", indent - 2, NMTUtil::flag_to_name(flag));
     print_virtual_memory_diff(current_reserved_amount, current_committed_amount,
       early_reserved_amount, early_committed_amount);
     out->print_cr(")");
 
+    streamIndentor si(out, indent);
+
     // detail lines
     if (flag == mtClass) {
       // report class count
-      out->print("%27s (classes #" SIZE_FORMAT "", " ", _current_baseline.class_count());
+      out->print("(classes #" SIZE_FORMAT, _current_baseline.class_count());
       const ssize_t class_count_diff =
           counter_diff(_current_baseline.class_count(), _early_baseline.class_count());
       if (class_count_diff != 0) {
@@ -597,7 +632,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
       }
       out->print_cr(")");
 
-      out->print("%27s (  instance classes #" SIZE_FORMAT, " ", _current_baseline.instance_class_count());
+      out->print("(  instance classes #" SIZE_FORMAT, _current_baseline.instance_class_count());
       const ssize_t instance_class_count_diff =
           counter_diff(_current_baseline.instance_class_count(), _early_baseline.instance_class_count());
       if (instance_class_count_diff != 0) {
@@ -613,14 +648,14 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
 
     } else if (flag == mtThread) {
       // report thread count
-      out->print("%27s (threads #" SIZE_FORMAT "", " ", _current_baseline.thread_count());
+      out->print("(threads #" SIZE_FORMAT, _current_baseline.thread_count());
       const ssize_t thread_count_diff = counter_diff(_current_baseline.thread_count(), _early_baseline.thread_count());
       if (thread_count_diff != 0) {
         out->print(" " SSIZE_PLUS_FORMAT, thread_count_diff);
       }
       out->print_cr(")");
 
-      out->print("%27s (stack: ", " ");
+      out->print("(stack: ");
       // report thread stack
       const VirtualMemory* current_thread_stack =
         _current_baseline.virtual_memory(mtThreadStack);
@@ -638,7 +673,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
     size_t early_malloc_amount   = early_malloc->malloc_size();
     if (amount_in_current_scale(current_malloc_amount) > 0 ||
         diff_in_current_scale(current_malloc_amount, early_malloc_amount) != 0) {
-      out->print("%28s(", " ");
+      out->print("(");
       print_malloc_diff(current_malloc_amount, (flag == mtChunk) ? 0 : current_malloc->malloc_count(),
         early_malloc_amount, early_malloc->malloc_count(), mtNone);
       out->print_cr(")");
@@ -647,7 +682,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
     // Report virtual memory
     if (amount_in_current_scale(current_vm->reserved()) > 0 ||
         diff_in_current_scale(current_vm->reserved(), early_vm->reserved()) != 0) {
-      out->print("%27s (mmap: ", " ");
+      out->print("(mmap: ");
       print_virtual_memory_diff(current_vm->reserved(), current_vm->committed(),
         early_vm->reserved(), early_vm->committed());
       out->print_cr(")");
@@ -656,7 +691,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
     // Report arena memory
     if (amount_in_current_scale(current_malloc->arena_size()) > 0 ||
         diff_in_current_scale(current_malloc->arena_size(), early_malloc->arena_size()) != 0) {
-      out->print("%28s(", " ");
+      out->print("(");
       print_arena_diff(current_malloc->arena_size(), current_malloc->arena_count(),
         early_malloc->arena_size(), early_malloc->arena_count());
       out->print_cr(")");
@@ -667,8 +702,8 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
       size_t current_tracking_overhead = amount_in_current_scale(_current_baseline.malloc_tracking_overhead());
       size_t early_tracking_overhead   = amount_in_current_scale(_early_baseline.malloc_tracking_overhead());
 
-      out->print("%27s (tracking overhead=" SIZE_FORMAT "%s", " ",
-        amount_in_current_scale(_current_baseline.malloc_tracking_overhead()), scale);
+      out->print("(tracking overhead=" SIZE_FORMAT "%s",
+                 amount_in_current_scale(_current_baseline.malloc_tracking_overhead()), scale);
 
       int64_t overhead_diff = diff_in_current_scale(_current_baseline.malloc_tracking_overhead(),
                                                     _early_baseline.malloc_tracking_overhead());
@@ -679,7 +714,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
     } else if (flag == mtClass) {
       print_metaspace_diff(current_ms, early_ms);
     }
-    out->print_cr(" ");
+    out->cr();
   }
 }
 
@@ -697,8 +732,8 @@ void MemSummaryDiffReporter::print_metaspace_diff(const char* header,
   outputStream* out = output();
   const char* scale = current_scale();
 
-  out->print_cr("%27s: (  %s)", " ", header);
-  out->print("%27s (    ", " ");
+  out->print_cr("(  %s)", header);
+  out->print("(    ");
   print_virtual_memory_diff(current_stats.reserved(),
                             current_stats.committed(),
                             early_stats.reserved(),
@@ -713,8 +748,8 @@ void MemSummaryDiffReporter::print_metaspace_diff(const char* header,
   int64_t diff_waste = diff_in_current_scale(current_waste, early_waste);
 
   // Diff used
-  out->print("%27s (    used=" SIZE_FORMAT "%s", " ",
-    amount_in_current_scale(current_stats.used()), scale);
+  out->print("(    used=" SIZE_FORMAT "%s",
+             amount_in_current_scale(current_stats.used()), scale);
   if (diff_used != 0) {
     out->print(" " INT64_PLUS_FORMAT "%s", diff_used, scale);
   }
@@ -723,8 +758,8 @@ void MemSummaryDiffReporter::print_metaspace_diff(const char* header,
   // Diff waste
   const float waste_percentage = current_stats.committed() == 0 ? 0.0f :
                                  ((float)current_waste * 100.0f) / (float)current_stats.committed();
-  out->print("%27s (    waste=" SIZE_FORMAT "%s =%2.2f%%", " ",
-    amount_in_current_scale(current_waste), scale, waste_percentage);
+  out->print("(    waste=" SIZE_FORMAT "%s =%2.2f%%",
+             amount_in_current_scale(current_waste), scale, waste_percentage);
   if (diff_waste != 0) {
     out->print(" " INT64_PLUS_FORMAT "%s", diff_waste, scale);
   }
@@ -841,11 +876,13 @@ void MemDetailDiffReporter::diff_malloc_site(const NativeCallStack* stack, size_
   }
 
   stack->print_on(out);
-  out->print("%28s (", " ");
-  print_malloc_diff(current_size, current_count,
-    early_size, early_count, flags);
+  INDENT_BY(28,
+    out->print("(");
+    print_malloc_diff(current_size, current_count, early_size, early_count, flags);
+    out->print_cr(")");
+  )
+  out->cr();
 
-  out->print_cr(")\n");
 }
 
 
@@ -874,22 +911,14 @@ void MemDetailDiffReporter::diff_virtual_memory_site(const NativeCallStack* stac
   }
 
   stack->print_on(out);
-  out->print("%28s (mmap: ", " ");
-  print_virtual_memory_diff(current_reserved, current_committed,
-    early_reserved, early_committed);
-
-  if (flag != mtNone) {
-    out->print(" Type=%s", NMTUtil::flag_to_name(flag));
-  }
-
-  out->print_cr(")\n");
+  INDENT_BY(28,
+    out->print("(mmap: ");
+    print_virtual_memory_diff(current_reserved, current_committed, early_reserved, early_committed);
+    if (flag != mtNone) {
+      out->print(" Type=%s", NMTUtil::flag_to_name(flag));
+    }
+    out->print_cr(")");
+  )
+  out->cr();
 }
 
-void MemDetailReporter::report_memory_file_allocations() {
-  stringStream st;
-  {
-    MemoryFileTracker::Instance::Locker lock;
-    MemoryFileTracker::Instance::print_all_reports_on(&st, scale());
-  }
-  output()->print_raw(st.freeze());
-}
diff --git a/src/hotspot/share/nmt/memReporter.hpp b/src/hotspot/share/nmt/memReporter.hpp
index 9355704531c..3f08ecbd284 100644
--- a/src/hotspot/share/nmt/memReporter.hpp
+++ b/src/hotspot/share/nmt/memReporter.hpp
@@ -44,9 +44,8 @@ class MemReporterBase : public StackObj {
   // Default scale to use if no scale given.
   static const size_t default_scale = K;
 
-  MemReporterBase(outputStream* out, size_t scale = default_scale) :
-    _scale(scale), _output(out)
-  {}
+  MemReporterBase(outputStream* out, size_t scale = default_scale);
+  ~MemReporterBase();
 
   // Helper functions
   // Calculate total reserved and committed amount
@@ -109,10 +108,7 @@ class MemReporterBase : public StackObj {
   void print_total(size_t reserved, size_t committed, size_t peak = 0) const;
   void print_malloc(const MemoryCounter* c, MEMFLAGS flag = mtNone) const;
   void print_virtual_memory(size_t reserved, size_t committed, size_t peak) const;
-
-  void print_malloc_line(const MemoryCounter* c) const;
-  void print_virtual_memory_line(size_t reserved, size_t committed, size_t peak) const;
-  void print_arena_line(const MemoryCounter* c) const;
+  void print_arena(const MemoryCounter* c) const;
 
   void print_virtual_memory_region(const char* type, address base, size_t size) const;
 };
diff --git a/src/hotspot/share/nmt/memoryFileTracker.cpp b/src/hotspot/share/nmt/memoryFileTracker.cpp
index a92f8e9f538..25f2667e5c3 100644
--- a/src/hotspot/share/nmt/memoryFileTracker.cpp
+++ b/src/hotspot/share/nmt/memoryFileTracker.cpp
@@ -92,7 +92,10 @@ void MemoryFileTracker::print_report_on(const MemoryFile* file, outputStream* st
                        NMTUtil::amount_in_scale(end_addr - start_addr, scale),
                        NMTUtil::scale_name(scale),
                        NMTUtil::flag_to_name(prev->val().out.flag()));
-      _stack_storage.get(prev->val().out.stack()).print_on(stream, 4);
+      {
+        streamIndentor si(stream, 4);
+        _stack_storage.get(prev->val().out.stack()).print_on(stream);
+      }
       stream->cr();
     }
     prev = current;
diff --git a/src/hotspot/share/utilities/nativeCallStack.cpp b/src/hotspot/share/utilities/nativeCallStack.cpp
index 3ddf296506c..873f3856b74 100644
--- a/src/hotspot/share/utilities/nativeCallStack.cpp
+++ b/src/hotspot/share/utilities/nativeCallStack.cpp
@@ -71,24 +71,18 @@ int NativeCallStack::frames() const {
   return index;
 }
 
-void NativeCallStack::print_on(outputStream* out) const {
-  print_on(out, 0);
-}
-
 // Decode and print this call path
-void NativeCallStack::print_on(outputStream* out, int indent) const {
+void NativeCallStack::print_on(outputStream* out) const {
   DEBUG_ONLY(assert_not_fake();)
   address pc;
   char    buf[1024];
   int     offset;
   if (is_empty()) {
-    out->fill_to(indent);
     out->print("[BOOTSTRAP]");
   } else {
     for (int frame = 0; frame < NMT_TrackingStackDepth; frame ++) {
       pc = get_frame(frame);
       if (pc == nullptr) break;
-      out->fill_to(indent);
       out->print("[" PTR_FORMAT "]", p2i(pc));
       // Print function and library; shorten library name to just its last component
       // for brevity, and omit it completely for libjvm.so
diff --git a/src/hotspot/share/utilities/nativeCallStack.hpp b/src/hotspot/share/utilities/nativeCallStack.hpp
index 43e90512b52..6c04169146e 100644
--- a/src/hotspot/share/utilities/nativeCallStack.hpp
+++ b/src/hotspot/share/utilities/nativeCallStack.hpp
@@ -124,7 +124,6 @@ public:
   }
 
   void print_on(outputStream* out) const;
-  void print_on(outputStream* out, int indent) const;
 };
 
 #define FAKE_CALLSTACK NativeCallStack(NativeCallStack::FakeMarker::its_fake)
diff --git a/src/hotspot/share/utilities/ostream.cpp b/src/hotspot/share/utilities/ostream.cpp
index 858f6798e28..72df31e12f9 100644
--- a/src/hotspot/share/utilities/ostream.cpp
+++ b/src/hotspot/share/utilities/ostream.cpp
@@ -44,18 +44,11 @@
 extern "C" void jio_print(const char* s, size_t len);
 extern "C" int jio_printf(const char *fmt, ...);
 
-outputStream::outputStream() {
-  _position    = 0;
-  _precount    = 0;
-  _indentation = 0;
-  _scratch     = nullptr;
-  _scratch_len = 0;
-}
-
 outputStream::outputStream(bool has_time_stamps) {
   _position    = 0;
   _precount    = 0;
   _indentation = 0;
+  _autoindent  = false;
   _scratch     = nullptr;
   _scratch_len = 0;
   if (has_time_stamps)  _stamp.update();
@@ -159,6 +152,9 @@ void outputStream::do_vsnprintf_and_write_with_scratch_buffer(const char* format
 }
 
 void outputStream::do_vsnprintf_and_write(const char* format, va_list ap, bool add_cr) {
+  if (_autoindent && _position == 0) {
+    indent();
+  }
   if (_scratch) {
     do_vsnprintf_and_write_with_scratch_buffer(format, ap, add_cr);
   } else {
@@ -188,6 +184,13 @@ void outputStream::vprint_cr(const char* format, va_list argptr) {
   do_vsnprintf_and_write(format, argptr, true);
 }
 
+void outputStream::print_raw(const char* str, size_t len) {
+  if (_autoindent && _position == 0) {
+    indent();
+  }
+  write(str, len);
+}
+
 void outputStream::fill_to(int col) {
   int need_fill = col - position();
   sp(need_fill);
diff --git a/src/hotspot/share/utilities/ostream.hpp b/src/hotspot/share/utilities/ostream.hpp
index 98ea9f54a34..b4ce7c32c60 100644
--- a/src/hotspot/share/utilities/ostream.hpp
+++ b/src/hotspot/share/utilities/ostream.hpp
@@ -46,9 +46,10 @@ DEBUG_ONLY(class ResourceMark;)
 class outputStream : public CHeapObjBase {
  private:
    NONCOPYABLE(outputStream);
+   int _indentation; // current indentation
+   bool _autoindent; // if true, every line starts with indentation
 
  protected:
-   int _indentation; // current indentation
    int _position;    // visual position on the current line
    uint64_t _precount; // number of chars output, less than _position
    TimeStamp _stamp; // for time stamps
@@ -90,8 +91,7 @@ class outputStream : public CHeapObjBase {
    class TestSupport;  // Unit test support
 
    // creation
-   outputStream();
-   outputStream(bool has_time_stamps);
+   outputStream(bool has_time_stamps = false);
 
    // indentation
    outputStream& indent();
@@ -104,6 +104,13 @@ class outputStream : public CHeapObjBase {
    void fill_to(int col);
    void move_to(int col, int slop = 6, int min_space = 2);
 
+   // Automatic indentation:
+   // If autoindent mode is on, the following APIs will automatically indent
+   // line starts depending on the current indentation level:
+   // print(), print_cr(), print_raw(), print_raw_cr()
+   // Other APIs are unaffected
+   void set_autoindent(bool value) { _autoindent = value; }
+
    // sizing
    int position() const { return _position; }
    julong count() const { return _precount + _position; }
@@ -119,10 +126,10 @@ class outputStream : public CHeapObjBase {
    void print_cr(const char* format, ...) ATTRIBUTE_PRINTF(2, 3);
    void vprint(const char *format, va_list argptr) ATTRIBUTE_PRINTF(2, 0);
    void vprint_cr(const char* format, va_list argptr) ATTRIBUTE_PRINTF(2, 0);
-   void print_raw(const char* str) { write(str, strlen(str)); }
-   void print_raw(const char* str, size_t len) { write(str, len); }
-   void print_raw_cr(const char* str) { write(str, strlen(str)); cr(); }
-   void print_raw_cr(const char* str, size_t len){ write(str, len); cr(); }
+   void print_raw(const char* str)                { print_raw(str, strlen(str)); }
+   void print_raw(const char* str, size_t len);
+   void print_raw_cr(const char* str)             { print_raw(str); cr(); }
+   void print_raw_cr(const char* str, size_t len) { print_raw(str, len); cr(); }
    void print_data(void* data, size_t len, bool with_ascii, bool rel_addr=true);
    void put(char ch);
    void sp(int count = 1);
diff --git a/test/hotspot/gtest/utilities/test_ostream.cpp b/test/hotspot/gtest/utilities/test_ostream.cpp
index 270ba59212a..26fd9d227c1 100644
--- a/test/hotspot/gtest/utilities/test_ostream.cpp
+++ b/test/hotspot/gtest/utilities/test_ostream.cpp
@@ -104,6 +104,53 @@ TEST_VM(ostream, bufferedStream_dynamic_small) {
   }
 }
 
+static void test_autoindent(bool on) {
+  stringStream ss;
+  ss.set_autoindent(on);
+  {
+    streamIndentor si(&ss, 5);
+    ss.print("ABC");
+    ss.print("DEF");
+    ss.cr();
+    ss.print_cr("0123");
+    {
+      streamIndentor si(&ss, 5);
+      ss.print_cr("4567");
+      ss.print_raw("89AB");
+      ss.print_raw("CDEXXXX", 3);
+      ss.print_raw_cr("XYZ");
+    }
+    ss.print("%u", 100);
+    ss.print_raw("KB");
+    ss.cr();
+  }
+  ss.print("end");
+
+  if (on) {
+    EXPECT_STREQ(ss.base(),
+        "     ABCDEF\n"
+        "     0123\n"
+        "          4567\n"
+        "          89ABCDEXYZ\n"
+        "     100KB\n"
+        "end"
+    );
+  } else {
+    // no autoindent: calls should work as always without indentation
+    EXPECT_STREQ(ss.base(),
+        "ABCDEF\n"
+        "0123\n"
+        "4567\n"
+        "89ABCDEXYZ\n"
+        "100KB\n"
+        "end"
+    );
+  }
+}
+
+TEST_VM(ostream, autoindent_on)  { test_autoindent(true);  }
+TEST_VM(ostream, autoindent_off) { test_autoindent(false); }
+
 /* Activate to manually test bufferedStream dynamic cap.
 
 TEST_VM(ostream, bufferedStream_dynamic_large) {