Android go低内存配置--对系统的影响

作者 by adtxl / 2022-03-22 / 暂无评论 / 704 个足迹

接上文:Android go 低内存配置--简介

1. ro.config.low_ram

Android go该属性将为true,app可以通过判断这个属性确定是否为低内存设备,从而采取一些省内存的操作。

# Set lowram options and enable traced by default
PRODUCT_VENDOR_PROPERTIES += \
     ro.config.low_ram=true \

系统中针对该属性的操作主要如下:

1.1 logcat

根据文件system/logging/logd/README.property,

ro.config.low_ram          bool   false  if true, logd.statistics,
                                         ro.logd.kernel default false,
                                         logd.size 64K instead of 256K.
  1. logd.statistics为false,"logd.statistics"默认为svelte+,功能为Enable logcat -S statistics,统计log信息。
  2. ro.logd.kernel为false,不能使用logcat看kernel的log
  3. 将logd的size默认改小为64K

1.2 lmkd

1.2.1 low_ram_device & per_app_memcg

在lmkd的属性处理中,当为低内存设备时,per_app_config的默认值为true.
并影响一些lmkd的参数,可参考:

属性 使用 默认(High performance) 默认(low ram)
ro.lmk.psi_partial_stall_ms 部分 PSI 失速阈值(以毫秒为单位),用于触发内存不足通知。 70 200
ro.lmk.psi_complete_stall_ms 完全 PSI 失速阈值(以毫秒为单位),用于触发critical内存通知 700 700
ro.lmk.thrashing_limit 工作集 refault 数量的上限 100 30
ro.lmk.thrashing_limit_decay 抖动阈值衰减 10 50
ro.lmk.swap_util_max 最大交换内存量 100 100
ro.lmk.swap_free_low_percentage 可用交换水平, 20 10

下面分析下当per_app_memcg属性为true时主要做了什么?

libprocessgroup会根据'ro.config.per_app_memcg'属性判断使用使用per-app-memcg,如果未设置该属性。在低内存设备会默认使用per-app-memcg,否则为false.然后libprocessgroup会创建相关的节点.

cmd_procprio的主要作用是更新进程的的adj,首先通过lmkd_pack_get_procprio将packet解释为lmk_procprio类型的数据,该数据类型包括pid,uid以及adj,接着检查需要设置的adj是否在范围以内,最后写入到/proc/[pid]/oom_score_adj.至此如果是使用内核逻辑的话,就会返回.否则还会进行进一步处理.
假如是low_ram_device,还会去更新soft_limit_mult/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_byte.最后还会通过调用pid_lookup在哈希表中是否存在该进程,假如不存在,则将进程加入到双向链表中,否则将该进程移出,并重新加入到双向链表中的头部.

memory.soft_limit_in_byte具体有什么用下面会继续分析

static void cmd_procprio(LMKD_CTRL_PACKET packet, int field_count, struct ucred *cred) {

......

    /* lmkd should not change soft limits for services */
    if (params.ptype == PROC_TYPE_APP && per_app_memcg) {
        if (params.oomadj >= 900) {
            soft_limit_mult = 0;
        } else if (params.oomadj >= 800) {
            soft_limit_mult = 0;
        } else if (params.oomadj >= 700) {
            soft_limit_mult = 0;
        } else if (params.oomadj >= 600) {
            // Launcher should be perceptible, don't kill it.
            params.oomadj = 200;
            soft_limit_mult = 1;
        } else if (params.oomadj >= 500) {
            soft_limit_mult = 0;
        } else if (params.oomadj >= 400) {
            soft_limit_mult = 0;
        } else if (params.oomadj >= 300) {
            soft_limit_mult = 1;
        } else if (params.oomadj >= 200) {
            soft_limit_mult = 8;
        } else if (params.oomadj >= 100) {
            soft_limit_mult = 10;
        } else if (params.oomadj >=   0) {
            soft_limit_mult = 20;
        } else {
            // Persistent processes will have a large
            // soft limit 512MB.
            soft_limit_mult = 64;
        }

        snprintf(path, sizeof(path), MEMCG_SYSFS_PATH
                 "apps/uid_%d/pid_%d/memory.soft_limit_in_bytes",
                 params.uid, params.pid);
        snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);

        /*
         * system_server process has no memcg under /dev/memcg/apps but should be
         * registered with lmkd. This is the best way so far to identify it.
         */
        is_system_server = (params.oomadj == SYSTEM_ADJ &&
                            (pwdrec = getpwnam("system")) != NULL &&
                            params.uid == pwdrec->pw_uid);
        writefilestring(path, val, !is_system_server);
    }

......

}

设置ro.config.per_app_memcg属性为false,将不会使用per_app_memcg。

1.2.2 art

如果ro.config.low_ram属性为true,此时会在虚拟机启动的时候添加LowMemoryMode参数。

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote)
{
......
    property_get("ro.config.low_ram", propBuf, "");
    if (strcmp(propBuf, "true") == 0) {
      addOption("-XX:LowMemoryMode");
    }
......
}

在下面的函数中会解析出来,如果是低内存设备,background_collector_type_为kCollectorTypeSS。

// art/runtime/parsed_options.cc

bool ParsedOptions::DoParse(const RuntimeOptions& options,
                            bool ignore_unrecognized,
                            RuntimeArgumentMap* runtime_options) {
......
    if (background_collector_type_ == gc::kCollectorTypeNone) {
      background_collector_type_ = low_memory_mode_ ?
          gc::kCollectorTypeSS : gc::kCollectorTypeHomogeneousSpaceCompact;
    }
......

}

还有下面的影响,不太清楚

// art/runtime/runtime.cc

static constexpr double kLowMemoryMinLoadFactor = 0.5;
static constexpr double kLowMemoryMaxLoadFactor = 0.8;
static constexpr double kNormalMinLoadFactor = 0.4;
static constexpr double kNormalMaxLoadFactor = 0.7;

bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {



  float foreground_heap_growth_multiplier;
  if (is_low_memory_mode_ && !runtime_options.Exists(Opt::ForegroundHeapGrowthMultiplier)) {
    // If low memory mode, use 1.0 as the multiplier by default.
    foreground_heap_growth_multiplier = 1.0f;
  } else {
    foreground_heap_growth_multiplier =
        runtime_options.GetOrDefault(Opt::ForegroundHeapGrowthMultiplier) +
            kExtraDefaultHeapGrowthMultiplier;
  }
}


double Runtime::GetHashTableMinLoadFactor() const {
  return is_low_memory_mode_ ? kLowMemoryMinLoadFactor : kNormalMinLoadFactor;
}

double Runtime::GetHashTableMaxLoadFactor() const {
  return is_low_memory_mode_ ? kLowMemoryMaxLoadFactor : kNormalMaxLoadFactor;
}

2. speed-profile

# Speed profile services and wifi-service to reduce RAM and storage.
PRODUCT_SYSTEM_SERVER_COMPILER_FILTER := speed-profile

根据https://source.android.com/devices/tech/dalvik/configure
dalvik.vm.systemservercompilerfilter: the compiler filter that the device will use when recompiling system server
默认会使用speed(运行 DEX 代码验证,并对所有方法进行 AOT 编译),这里使用speed-profile(行 DEX 代码验证,并对配置文件中列出的方法进行 AOT 编译),可以节省点内存。

# build/make/core/main.mk
# Add the system server compiler filter if they are specified for the product.
ifneq (,$(PRODUCT_SYSTEM_SERVER_COMPILER_FILTER))
ADDITIONAL_PRODUCT_PROPERTIES += dalvik.vm.systemservercompilerfilter=$(PRODUCT_SYSTEM_SERVER_COMPILER_FILTER)
endif

3. PRODUCT_ALWAYS_PREOPT_EXTRACTED_APK

总是预先选择提取的apk,以防止从gms模块的APK中提取.防止执行apk导致占用内存变多?

# Always preopt extracted APKs to prevent extracting out of the APK for gms
# modules.
PRODUCT_ALWAYS_PREOPT_EXTRACTED_APK := true

设置该属性后,my_preopt_for_extracted_apk=true,

# build/core/app_prebuilt_internal.mk
ifeq ($(PRODUCT_ALWAYS_PREOPT_EXTRACTED_APK),true)
# If the product property is set, always preopt for extracted modules to prevent executing out of
# the APK.
my_preopt_for_extracted_apk := true
endif


# build/make/core/dex_preopt_odex_install.mk
# if WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY=true and module is not in boot class path skip
# Also preopt system server jars since selinux prevents system server from loading anything from
# /data. If we don't do this they will need to be extracted which is not favorable for RAM usage
# or performance. If my_preopt_for_extracted_apk is true, we ignore the only preopt boot image
# options.
system_server_jars := $(foreach m,$(PRODUCT_SYSTEM_SERVER_JARS),$(call word-colon,2,$(m)))
ifneq (true,$(my_preopt_for_extracted_apk))
  ifeq (true,$(WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY))
    ifeq ($(filter $(system_server_jars) $(DEXPREOPT_BOOT_JARS_MODULES),$(LOCAL_MODULE)),)
      LOCAL_DEX_PREOPT :=
    endif
  endif
endif

4. boot_image_profile

根据注释这里并没有针对Android go做特别的优化。不过如果有需要应该可以在这里做优化。

# Use a profile based boot image for this device. Note that this is currently a
# generic profile and not Android Go optimized.
PRODUCT_USE_PROFILE_FOR_BOOT_IMAGE := true
PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION := frameworks/base/config/boot-image-profile.txt

5. libartd

不生成libaard,属于节省内存的操作了,可能影响debug.

# Do not generate libartd.
PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD := false

6. in-process APK

属于省内存操作?

# Do not spin up a separate process for the network stack on go devices, use an in-process APK.
PRODUCT_PACKAGES += InProcessNetworkStack
PRODUCT_PACKAGES += CellBroadcastAppPlatform
PRODUCT_PACKAGES += CellBroadcastServiceModulePlatform
PRODUCT_PACKAGES += com.android.tethering.inprocess

7. JAVA DEBUG INFO

省内存操作,影响调试。

# Strip the local variable table and the local variable type table to reduce
# the size of the system image. This has no bearing on stack traces, but will
# leave less information available via JDWP.
PRODUCT_MINIMIZE_JAVA_DEBUG_INFO := true

8. scudo

在eng版本中不使用scudo,scudo为了安全会多使用点内存

# Disable Scudo outside of eng builds to save RAM.
ifneq (,$(filter eng, $(TARGET_BUILD_VARIANT)))
  PRODUCT_DISABLE_SCUDO := true
endif

9. 一些系统属性

# 下面四个属性对使用psi策略的Android11&12是无用的
ro.lmk.critical_upgrade=true
ro.lmk.upgrade_pressure=40
ro.lmk.downgrade_pressure=60
ro.lmk.kill_heaviest_task=false
# set threshold to filter unused apps
pm.dexopt.downgrade_after_inactive_days=10
# set the compiler filter for shared apks to quicken.
# Rationale: speed has a lot of dex code expansion, it uses more ram and space
# compared to quicken. Using quicken for shared APKs on Go devices may save RAM.
# Note that this is a trade-off: here we trade clean pages for dirty pages,
# extra cpu and battery. That's because the quicken files will be jit-ed in all
# the processes that load of shared apk and the code cache is not shared.
# Some notable apps that will be affected by this are gms and chrome.
# b/65591595.
# 默认是speed
pm.dexopt.shared=quicken
# Default heap sizes. Allow up to 256m for large heaps to make sure a single app
# doesn't take all of the RAM.
# 开发板这里默认dalvik.vm.heapgrowthlimit=128m,dalvik.vm.heapsize=174m,下面俩设置似乎也没什么用
dalvik.vm.heapgrowthlimit=128m
dalvik.vm.heapsize=256m

10. handheld_core_hardware.xml

这个文件包含系统的核心硬件模块配置文件.CTS测试时会使用,Android go的这个文件内容相对少一点,对机器要求低点。

# use the go specific handheld_core_hardware.xml from frameworks
PRODUCT_COPY_FILES += \
    frameworks/native/data/etc/go_handheld_core_hardware.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/handheld_core_hardware.xml

简单看了下,go_handheld_core_hardware.xml里差异如下

// go 里多的
    <!-- Indicate support for the Android security model per the CDD. -->
    <feature name="android.hardware.security.model.compatible" />

// go里没有
    <feature name="android.software.app_widgets" />
    <feature name="android.software.voice_recognizers" notLowRam="true" />
    <feature name="android.software.picture_in_picture" notLowRam="true" />
    <feature name="android.software.activities_on_secondary_displays" notLowRam="true" />
    <feature name="android.software.managed_users" notLowRam="true"/>

11. TARGET_VNDK_USE_CORE_VARIANT

对vndk的影响,删除具有相同核心变体的重复数据删除VNDK库

# Dedupe VNDK libraries with identical core variants.
TARGET_VNDK_USE_CORE_VARIANT := true

独特见解