Make kHugeNativeAllocs configurable

Add -XX:StopForNativeAllocs command line argument to replace
kHugeNativeAllocs constant.

The default remains at 1 GB. But This default will allow small
memory devices to run out of memory if we allocate Java owned native
memory faster than what the GC can keep up with. Setting it to
a smaller value should prevent that.

Bug: 122552730
Test: Boot AOSP, Treehugger

Change-Id: I5c84b2f1f67038e1b7a0ca3f5fc08090359c5f3e
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 9126d90..9755016 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -173,6 +173,7 @@
            size_t max_free,
            double target_utilization,
            double foreground_heap_growth_multiplier,
+           size_t stop_for_native_allocs,
            size_t capacity,
            size_t non_moving_space_capacity,
            const std::vector<std::string>& boot_class_path,
@@ -272,6 +273,7 @@
       max_free_(max_free),
       target_utilization_(target_utilization),
       foreground_heap_growth_multiplier_(foreground_heap_growth_multiplier),
+      stop_for_native_allocs_(stop_for_native_allocs),
       total_wait_time_(0),
       verify_object_mode_(kVerifyObjectModeDisabled),
       disable_moving_gc_count_(0),
@@ -3749,15 +3751,9 @@
 static constexpr size_t kNewNativeDiscountFactor = 2;
 
 // If weighted java + native memory use exceeds our target by kStopForNativeFactor, and
-// newly allocated memory exceeds kHugeNativeAlloc, we wait for GC to complete to avoid
+// newly allocated memory exceeds stop_for_native_allocs_, we wait for GC to complete to avoid
 // running out of memory.
 static constexpr float kStopForNativeFactor = 4.0;
-// TODO: Allow this to be tuned. We want this much smaller for some apps, like Calculator.
-// But making it too small can cause jank in apps like launcher that intentionally allocate
-// large amounts of memory in rapid succession. (b/122099093)
-// For now, we punt, and use a value that should be easily large enough to disable this in all
-// questionable setting, but that is clearly too large to be effective for small memory devices.
-static constexpr size_t kHugeNativeAllocs = 1 * GB;
 
 // Return the ratio of the weighted native + java allocated bytes to its target value.
 // A return value > 1.0 means we should collect. Significantly larger values mean we're falling
@@ -3797,7 +3793,7 @@
     if (is_gc_concurrent) {
       RequestConcurrentGC(self, kGcCauseForNativeAlloc, /*force_full=*/true);
       if (gc_urgency > kStopForNativeFactor
-          && current_native_bytes > kHugeNativeAllocs) {
+          && current_native_bytes > stop_for_native_allocs_) {
         // We're in danger of running out of memory due to rampant native allocation.
         if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
           LOG(INFO) << "Stopping for native allocation, urgency: " << gc_urgency;
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index b474050..7c5ae17 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -187,6 +187,7 @@
        size_t max_free,
        double target_utilization,
        double foreground_heap_growth_multiplier,
+       size_t stop_for_native_allocs,
        size_t capacity,
        size_t non_moving_space_capacity,
        const std::vector<std::string>& boot_class_path,
@@ -1440,6 +1441,13 @@
   // How much more we grow the heap when we are a foreground app instead of background.
   double foreground_heap_growth_multiplier_;
 
+  // The amount of native memory allocation since the last GC required to cause us to wait for a
+  // collection as a result of native allocation. Very large values can cause the device to run
+  // out of memory, due to lack of finalization to reclaim native memory.  Making it too small can
+  // cause jank in apps like launcher that intentionally allocate large amounts of memory in rapid
+  // succession. (b/122099093) 1/4 to 1/3 of physical memory seems to be a good number.
+  const size_t stop_for_native_allocs_;
+
   // Total time which mutators are paused or waiting for GC to complete.
   uint64_t total_wait_time_;
 
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 96c8e10..dd62382 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -138,6 +138,9 @@
       .Define("-XX:NonMovingSpaceCapacity=_")
           .WithType<MemoryKiB>()
           .IntoKey(M::NonMovingSpaceCapacity)
+      .Define("-XX:StopForNativeAllocs=_")
+          .WithType<MemoryKiB>()
+          .IntoKey(M::StopForNativeAllocs)
       .Define("-XX:HeapTargetUtilization=_")
           .WithType<double>().WithRange(0.1, 0.9)
           .IntoKey(M::HeapTargetUtilization)
@@ -739,6 +742,7 @@
   UsageMessage(stream, "  -XX:BackgroundGC=none\n");
   UsageMessage(stream, "  -XX:LargeObjectSpace={disabled,map,freelist}\n");
   UsageMessage(stream, "  -XX:LargeObjectThreshold=N\n");
+  UsageMessage(stream, "  -XX:StopForNativeAllocs=N\n");
   UsageMessage(stream, "  -XX:DumpNativeStackOnSigQuit=booleanvalue\n");
   UsageMessage(stream, "  -XX:MadviseRandomAccess:booleanvalue\n");
   UsageMessage(stream, "  -XX:SlowDebug={false,true}\n");
diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc
index ca2a4ea..75952bb 100644
--- a/runtime/parsed_options_test.cc
+++ b/runtime/parsed_options_test.cc
@@ -64,6 +64,7 @@
   options.push_back(std::make_pair("-Xmx4k", nullptr));
   options.push_back(std::make_pair("-Xss1m", nullptr));
   options.push_back(std::make_pair("-XX:HeapTargetUtilization=0.75", nullptr));
+  options.push_back(std::make_pair("-XX:StopForNativeAllocs=200m", nullptr));
   options.push_back(std::make_pair("-Dfoo=bar", nullptr));
   options.push_back(std::make_pair("-Dbaz=qux", nullptr));
   options.push_back(std::make_pair("-verbose:gc,class,jni", nullptr));
@@ -90,6 +91,7 @@
   EXPECT_PARSED_EQ(2048U, Opt::MemoryInitialSize);
   EXPECT_PARSED_EQ(4 * KB, Opt::MemoryMaximumSize);
   EXPECT_PARSED_EQ(1 * MB, Opt::StackSize);
+  EXPECT_PARSED_EQ(200 * MB, Opt::StopForNativeAllocs);
   EXPECT_DOUBLE_EQ(0.75, map.GetOrDefault(Opt::HeapTargetUtilization));
   EXPECT_TRUE(test_vfprintf == map.GetOrDefault(Opt::HookVfprintf));
   EXPECT_TRUE(test_exit == map.GetOrDefault(Opt::HookExit));
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index c21b2e4..28ec61a 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1293,6 +1293,7 @@
                        runtime_options.GetOrDefault(Opt::HeapMaxFree),
                        runtime_options.GetOrDefault(Opt::HeapTargetUtilization),
                        foreground_heap_growth_multiplier,
+                       runtime_options.GetOrDefault(Opt::StopForNativeAllocs),
                        runtime_options.GetOrDefault(Opt::MemoryMaximumSize),
                        runtime_options.GetOrDefault(Opt::NonMovingSpaceCapacity),
                        GetBootClassPath(),
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 4488680..a7b5f34 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -51,6 +51,7 @@
 RUNTIME_OPTIONS_KEY (MemoryKiB,           HeapMinFree,                    gc::Heap::kDefaultMinFree)
 RUNTIME_OPTIONS_KEY (MemoryKiB,           HeapMaxFree,                    gc::Heap::kDefaultMaxFree)
 RUNTIME_OPTIONS_KEY (MemoryKiB,           NonMovingSpaceCapacity,         gc::Heap::kDefaultNonMovingSpaceCapacity)
+RUNTIME_OPTIONS_KEY (MemoryKiB,           StopForNativeAllocs,            1 * GB)
 RUNTIME_OPTIONS_KEY (double,              HeapTargetUtilization,          gc::Heap::kDefaultTargetUtilization)
 RUNTIME_OPTIONS_KEY (double,              ForegroundHeapGrowthMultiplier, gc::Heap::kDefaultHeapGrowthMultiplier)
 RUNTIME_OPTIONS_KEY (unsigned int,        ParallelGCThreads,              0u)