diff --git a/libcxx/include/__config b/libcxx/include/__config
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -113,6 +113,8 @@
 #  define _LIBCPP_ABI_ENABLE_UNIQUE_PTR_TRIVIAL_ABI
 // Enable clang::trivial_abi on std::shared_ptr and std::weak_ptr
 #  define _LIBCPP_ABI_ENABLE_SHARED_PTR_TRIVIAL_ABI
+// Use exact string sizes on `resize()` rather than exponential growth.
+#  define _LIBCPP_ABI_EXACT_STRING_RESIZE
 #elif _LIBCPP_ABI_VERSION == 1
 #  if !defined(_LIBCPP_OBJECT_FORMAT_COFF)
 // Enable compiling copies of now inline methods into the dylib to support
diff --git a/libcxx/include/string b/libcxx/include/string
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -3276,9 +3276,13 @@
 basic_string<_CharT, _Traits, _Allocator>::resize(size_type __n, value_type __c)
 {
     size_type __sz = size();
-    if (__n > __sz)
+    if (__n > __sz) {
+#ifdef _LIBCPP_ABI_EXACT_STRING_RESIZE
+        if (__n > capacity())
+          __shrink_or_extend(__recommend(__n));
+#endif
         append(__n - __sz, __c);
-    else
+    } else
         __erase_to_end(__n);
 }
 
@@ -3288,7 +3292,11 @@
 {
     size_type __sz = size();
     if (__n > __sz) {
-       __append_default_init(__n - __sz);
+#ifdef _LIBCPP_ABI_EXACT_STRING_RESIZE
+      if (__n > capacity())
+        __shrink_or_extend(__recommend(__n));
+#endif
+      __append_default_init(__n - __sz);
     } else
         __erase_to_end(__n);
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.capacity/resize.exact.cpp b/libcxx/test/libcxx/strings/basic.string/string.capacity/resize.exact.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/string.capacity/resize.exact.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// void resize(size_type);
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ABI_EXACT_STRING_RESIZE
+
+#include <string>
+#include <stdexcept>
+#include <cassert>
+
+#include "test_macros.h"
+#include "min_allocator.h"
+
+bool is_between(size_t c, size_t low, size_t hi) { return c >= low && c <= hi; }
+
+template <class S>
+void test() {
+  // Tests that resize() uses exact growth rather than exponential growth.
+  const size_t kSsoSize = S().capacity();
+  const size_t kAlign = 16;
+  for (int i = kSsoSize + 1; i < 1 << 10; ++i) {
+    S s;
+    s.resize(i);
+    assert(s.size() == i);
+    assert(is_between(s.capacity(), i, i + kAlign));
+  }
+  for (int i = kSsoSize + 1; i < 1 << 10; ++i) {
+    S s;
+    for (int j = 0; j < i; ++j) {
+      s.push_back(' ');
+    }
+    assert(s.size() == i);
+    const size_t prev_cap = s.capacity();
+    s.resize(prev_cap + 10);
+    assert(is_between(s.capacity(), prev_cap + 10, prev_cap + 10 + kAlign));
+  }
+}
+
+int main(int, char**) {
+  {
+    typedef std::string S;
+    test<S>();
+  }
+#if TEST_STD_VER >= 11
+  {
+    typedef min_allocator<char> A;
+    typedef std::basic_string<char, std::char_traits<char>, A> S;
+    test<S>();
+  }
+#endif
+
+  return 0;
+}