Android 6.0 开启Pre-optimization出现ClassNotFoundException异常

时间:2023-01-19 21:22:16
1、何为Pre-optimization
Pre-optimization针对的是整个system.img镜像,好处是可以减少系统启动时间


2、配置Pre-optimization
开启该功能只需在板级文件BoardConfig.mk设置:
WITH_DEXPREOPT := true
更多配置参数及其详细内容见参考1


3、问题背景及描述
有一些定制的系统服务,所有服务代码统一放置在目录:
/framework/myservices
编译后生成jar包myservices.jar


然后在SystemServer.java(/framework/base/services/java/com/android/server)所在的Android.mk引用myservices.jar
LOCAL_JAVA_LIBRARIES += myservices
并在SystemServer.java中注册那些服务


由于系统core级别的jar需要提前加载,所以在环境变量BOOTCLASSPATH中增加myservices.jar
init.environ.rc
export BOOTCLASSPATH .....:/system/framework/myservices.jar



在不开启Pre-optimization(即WITH_DEXPREOPT := false)的情况下,系统可以正常运行;现在启用Pre-optimization,所用来源于myservices.jar的类都在系统启动过程中出现异常,类似:
E SystemServer: BOOT FAILURE starting MySecure Service
E SystemServer: java.lang.NoClassDefFoundError: Failed resolution of: Laa/bb/cc/MySecure;
E SystemServer: at com.android.server.SystemServer.startOtherServices(SystemServer.java:1034)
E SystemServer: at com.android.server.SystemServer.run(SystemServer.java:286)
E SystemServer: at com.android.server.SystemServer.main(SystemServer.java:178)
E SystemServer: at java.lang.reflect.Method.invoke(Native Method)
E SystemServer: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:772)
E SystemServer: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:662)
E SystemServer: Caused by: java.lang.ClassNotFoundException: Didn't find class "aa.bb.cc.MySecure" on path: DexPathList[[zip file "/system/framework/services.jar", zip file "/system/framework/ethernet-service.jar", zip file "/system/framework/wifi-service.jar", zip file "/system/framework/pppoe-service.jar"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
E SystemServer: at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
E SystemServer: at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
E SystemServer: at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
E SystemServer: ... 6 more
E SystemServer: Suppressed: java.lang.ClassNotFoundException: Didn't find class "aa.bb.cc.MySecure" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
E SystemServer: at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
E SystemServer: at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
E SystemServer: at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
E SystemServer: ... 7 more
E SystemServer: Suppressed: java.lang.ClassNotFoundException: aa.bb.cc.MySecure
E SystemServer: at java.lang.Class.classForName(Native Method)
E SystemServer: at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
E SystemServer: at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
E SystemServer: at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
E SystemServer: ... 8 more
E SystemServer: Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available


4、Pre-optimization开启前后编译差别
开启Pre-optimization后,每个模块会额外生成一个.odex文件,位于/system/framework/oat/arm/目录;
模块的一些信息被记录到boot.art和boot.oat文件中,位于/system/framework/arm/目录。
boot.art和boot.oat文件简介见参考2.


参考文章提到:boot.art里面使用的都是绝对地址。
绝对地址!
来,类比几个概念:
相对路径<--->绝对路径
相对地址<--->绝对地址
动态链接<--->静态链接
LOCAL_JAVA_LIBRARIES<--->LOCAL_STATIC_JAVA_LIBRARIES
这就不用解释了。
LOCAL_JAVA_LIBRARIES := myservices 改为>>>>LOCAL_STATIC_JAVA_LIBRARIES += myservices
就可以解决上述问题。


另:

我们发现,以telephony-common.jar为例,那些引用telephony-common.jar的使用的LOCAL_JAVA_LIBRARIES而并没有出现问题,所以我们可以推测还有其他方式解决类似问题。由于本人没打算深入研究ART虚拟机,就没有进一步研究。


附录1:

Configuring ART

Introduction


This document is intended to discuss how to configure ART and its compilation options. Topics addressed here include configuration of pre-compilation of the system image, dex2oat compilation options at first boot (and post-OTA), and how to trade off system partition space, data partition space, and performance.

See ART and Dalvik, the Dalvik Executable format, and the remaining pages on source.android.com to work with ART. See Verifying App Behavior on the Android Runtime (ART) to ensure your apps work properly.

How ART works


ART is the new Android runtime for the Android 5.0 (Lollipop or L) release and beyond. Dalvik is no longer available.

Please note, this section merely summarizes ART’s configuration. For an in-depth description, see the Android runtime presentation conducted at Google I/O 2014.

ART uses ahead-of-time (AOT) compilation. This means that, at installation, dex code is compiled to native code in OAT files, which replace Dalvik’s odex files. This has several implications:

  • Performance is improved over Dalvik. There is also a commensurate improvement in power consumption measured in the lab.
  • There is no runtime code cache. The OAT files are mapped to memory (and are thus page-able). The RAM memory footprint for OAT files might seem larger in terms of Proportional Set Size (PSS, or the memory shared across processes divided evenly between the processes). But because they are pageable we have found the system impact is improved in terms of real memory pressure effects as the Dalvik JIT cache was not pageable.
  • Similar to preloaded classes in the zygote, ART attempts to pre-initialize a set of classes at compile time. This creates a ‘boot.art’ file that comprises an image of the compacted heap of pre-initialized classes and related objects. This file is mapped into memory upon zygote startup. While this consumes additional storage (typically 10MB), it speeds zygote startup and creates opportunities for the system to swap out some preloaded classes under memory pressure. This also contributes to improved low-RAM performance for ART, since in Dalvik much of this class information would have been stored in dirty pages in the linear alloc space.
  • Dex file compilation uses a tool called dex2oat and takes more time than dexopt. The increase in time varies, but 2-3x increases in compile time are not unusual. For example, apps that typically take a second to install using dexopt might take 2-3 seconds.
  • OAT files are larger than odex files if full compilation is enabled. We discuss options to mitigate this cost later in this document.

Compilation options


Dex file compilation takes more time than dexopt, which can be noticeable when all of a user’s apps must be compiled during first boot (after factory reset or after receiving an OTA). To reduce the amount of compilation needed, ART supports the option of pre-optimizing libraries and applications in the system partition. Including the pre-optimized dex files takes space in the system image, so these options trade first boot time for system image size. Note that OTAs are relatively infrequent and subsequent boot times should be the same with or without pre-optimization.

WITH_DEXPREOPT

Pre-optimization is controlled by the build option WITH_DEXPREOPT. Before the L release, this was enabled by default in “user” builds. Starting in L, this option is opt-in and needs to be enabled in the product configuration such as a device’s BoardConfig.mk file.

Enabling WITH_DEXPREOPT causes everything in the system image to be pre-optimized. If this makes the system image too large, additional options can be specified to reduce the amount of pre-optimization. Note that all the following build options with “PREOPT” in the name must have WITH_DEXPREOPT enabled to work.

Example usage (in product’s BoardConfig.mk):

WITH_DEXPREOPT := true

DONT_DEXPREOPT_PREBUILTS

Enabling DONT_DEXPREOPT_PREBUILTS prevents the prebuilts from being pre-optimized. These are apps that have include $(BUILD_PREBUILT) specified in their Android.mk, such as Gmail. Skipping pre-optimization of prebuilt apps that are likely to be updated via Google Play saves /system space but does add to first boot time.

Example usage (in product’s BoardConfig.mk):

WITH_DEXPREOPT := true
DONT_DEXPREOPT_PREBUILTS := true

WITH_DEXPREOPT_BOOT_IMG_ONLY

Enabling WITH_DEXPREOPT_BOOT_IMG_ONLY only pre-optimizes the boot image, which consists of boot.art with the image classes and boot.oat with code for the boot classpath. Enabling this saves significant /system space but means all apps will be optimized at first boot. Typically it is better to selectively disable app pre-optimization via DONT_DEXPREOPT_PREBUILTS or add-product-dex-preopt-module-config.

Example usage (in product’s BoardConfig.mk):

WITH_DEXPREOPT := true
WITH_DEXPREOPT_BOOT_IMG_ONLY := true

LOCAL_DEX_PREOPT

Pre-optimization can also be enabled or disabled on an individual app basis by specifying the LOCAL_DEX_PREOPToption in the module definition. This can be useful for disabling pre-optimization of apps that may immediately receive Google Play updates since the updates would render the pre-optimized code in the system image obsolete. This is also useful to save space on major version upgrade OTAs since users may already have newer versions of apps in the data partition.

LOCAL_DEX_PREOPT supports the values ‘true’ or ‘false’ to enable or disable pre-optimization respectively. In addition, ‘nostripping’ can be specified if pre-optimization should not strip the classes.dex file from the apk or jar file. Normally this file is stripped since it’s no longer needed after pre-optimization, but this last option is necessary to allow third-party APK signatures to remain valid.

Example usage (in app’s Android.mk):

LOCAL_DEX_PREOPT := false

PRODUCT_DEX_PREOPT_*

Beginning post-L release in the Android Open Source Project (AOSP), a number of flags have been added that give further control to how pre-optimization is done. PRODUCT_DEX_PREOPT_BOOT_FLAGS passes options to dex2oat to control how the boot image is compiled. It can be used to specify customized image classes lists, compiled classes lists, and compiler filters, all of which are described in later sections. Similarly, PRODUCT_DEX_PREOPT_DEFAULT_FLAGS controls default flags to pass to dex2oat for compilation of everything besides the boot image, namely jar and apk files.

PRODUCT_DEX_PREOPT_MODULE_CONFIGS provides the ability to pass dex2oat options for a particular module and product configuration. It is set in a product’s device.mk file by $(call add-product-dex-preopt-module-config,<modules>,<option>) where <modules> is a list of LOCAL_MODULE and LOCAL_PACKAGE names for jar and apk files respectively. Through this flag, it is possible to have fine-grained control of pre-optimization for each dex file and a specific device. Such tuning allows /system space to be maximally used to improve first boot time.

Example usage (in product’s device.mk):

PRODUCT_DEX_PREOPT_DEFAULT_FLAGS := --compiler-filter=interpret-only
$(call add-product-dex-preopt-module-config,services,--compiler-filter=space)

These flags can also be used to selectively disable pre-optimization of a particular module or package by specifying $(call add-product-dex-preopt-module-config,<modules>,disable) in a product's device.mk file.

Example usage (in product’s device.mk):

$(call add-product-dex-preopt-module-config,Calculator,disable)

Preloaded Classes List


The preloaded classes list is a list of classes the zygote will initialize on startup. This saves each app from having to run these class initializers separately, allowing them to start up faster and share pages in memory. The preloaded classes list file is located at frameworks/base/preloaded-classes by default, and it contains a list that is tuned for typical phone use. This may be different for other devices, such as wearables, and should be tuned accordingly. Be careful when tuning this since adding too many classes wastes memory loading unused classes; meanwhile, adding too few forces each app to have to have its own copy, again wasting memory.

Example usage (in product’s device.mk):

PRODUCT_COPY_FILES += <filename>:system/etc/preloaded-classes

Note: This line must be placed before inheriting any product configuration makefiles that get the default one from build/target/product/base.mk.

Image Classes List


The image classes list is a list of classes that dex2oat initializes ahead of time and stores in the boot.art file. This allows the zygote to load these results out of the boot.art file on startup instead of running the initializers for these classes itself during preloading. A key feature of this is that the pages loaded from the image and shared between processes can be clean, allowing them to be swapped out easily in low-memory situations. In L, by default the image classes list uses the same list as the preloaded classes list. Beginning post-L in AOSP, a custom image classes list can be specified using PRODUCT_DEX_PREOPT_BOOT_FLAGS.

Example usage (in product’s device.mk):

PRODUCT_DEX_PREOPT_BOOT_FLAGS += --image-classes=<filename>

Compiled Classes List


In post-L AOSP, a subset of classes from the boot classpath can be specified to be compiled during pre-optimization using the compiled classes list. This can be a useful option for devices that are very tight on space and can’t fit the entire pre-optimized boot image. However, note classes not specified by this list will not be compiled - not even on the device - and must be interpreted, potentially affecting runtime performance. By default, dex2oat will look for a compiled classes list in $OUT/system/etc/compiled-classes, so a custom one can be copied to that location by the device.mk. A particular file location can also be specified using PRODUCT_DEX_PREOPT_BOOT_FLAGS.

Example usage (in product’s device.mk):

PRODUCT_COPY_FILES += <filename>:system/etc/compiled-classes

Note: This line must be placed before inheriting any product configuration makefiles that get the default one from build/target/product/base.mk.

Compiler Filters


In L, dex2oat takes a variety of --compiler-filter options to control how it compiles. Passing in a compiler filter flag for a particular app specifies how it’s pre-optimized. Here’s a description of each available option:

  • everything - compiles almost everything, excluding class initializers and some rare methods that are too large to be represented by the compiler’s internal representation.
  • speed - compiles most methods and maximizes runtime performance, which is the default option.
  • balanced - attempts to get the best performance return on compilation investment.
  • space - compiles a limited number of methods, prioritizing storage space.
  • interpret-only - skips all compilation and relies on the interpreter to run code.
  • verify-none - special option that skips verification and compilation, should be used only for trusted system code.

WITH_DEXPREOPT_PIC


In post-L AOSP, WITH_DEXPREOPT_PIC can be specified to enable position-independent code (PIC). With this, compiled code from the image doesn’t have to be relocated from /system into /data/dalvik-cache, saving space in the data partition. However, there is a slight runtime impact because it disables an optimization that takes advantage of position-dependent code. Typically, devices wanting to save space in /data should enable PIC compilation.

Example usage (in product’s device.mk):

WITH_DEXPREOPT := true
WITH_DEXPREOPT_PIC := true

WITH_ART_SMALL_MODE


For devices with very limited space, WITH_ART_SMALL_MODE can be enabled. This option compiles the boot classpath and nothing else, greatly reducing first boot time since most compilation is skipped. It also saves on storage because there is no compiled code for apps. However, this impacts runtime performance since app code has to be interpreted. The impact is limited since most performance sensitive code in the framework is still compiled, but regressions may appear in benchmarking.

Example usage (in product’s device.mk):

WITH_ART_SMALL_MODE := true

In future releases, this build option will be removed since it can be done with this (in product’s device.mk):

PRODUCT_PROPERTY_OVERRIDES += \
     dalvik.vm.dex2oat-filter=interpret-only \
     dalvik.vm.image-dex2oat-filter=speed

dalvik.vm Properties


Most dalvik.vm properties in ART are similar to Dalvik, but there are a few additional ones as described below. Note that these options affect dex2oat during on-device compilation as well as during pre-optimization, whereas most of the options discussed above affect only pre-optimization.

To control dex2oat while it’s compiling the boot image:

  • dalvik.vm.image-dex2oat-Xms: initial heap size
  • dalvik.vm.image-dex2oat-Xmx: maximum heap size
  • dalvik.vm.image-dex2oat-filter: compiler filter option

To control dex2oat while it’s compiling everything besides the boot image:

  • dalvik.vm.dex2oat-Xms: initial heap size
  • dalvik.vm.dex2oat-Xmx: maximum heap size
  • dalvik.vm.dex2oat-filter: compiler filter option

The options that control initial and maximum heap size for dex2oat should not be reduced since they could limit what applications can be compiled.

Sample Usage


The goal of these compiler options is to utilize available space in the system and data partition to reduce the amount of dex2oat that must be performed by the device.

For devices with ample system and data space, enabling dex pre-optimization is all that is necessary.

BoardConfig.mk:

WITH_DEXPREOPT := true

If this causes the system image to become too large, the next thing to try is disabling pre-optimization of the prebuilts.

BoardConfig.mk:

WITH_DEXPREOPT := true
DONT_DEXPREOPT_PREBUILTS := true

Again, if the system image is still too large, try pre-optimizing only the boot image.

BoardConfig.mk:

WITH_DEXPREOPT := true
WITH_DEXPREOPT_BOOT_IMG_ONLY := true

However, limiting to pre-optimizing only the boot-image means all apps will have to be optimized on first boot. In order to avoid this, it is possible to combine these high level flags with more fine-grained controls to maximize the amount of pre-optimized apps.

For instance, if disabling the pre-optimization of the prebuilts almost fits into the system partition, compiling the boot classpath with the ‘space’ option may make it fit. Note this compiles fewer methods in the boot classpath, potentially interpreting more code and impacting runtime performance.

BoardConfig.mk:

WITH_DEXPREOPT := true
DONT_DEXPREOPT_PREBUILTS := true

device.mk:

PRODUCT_DEX_PREOPT_BOOT_FLAGS := --compiler-filter=space

If a device has very limited system partition space, it’s possible to compile a subset of classes in the boot classpath using the compiled classes list. Boot classpath methods that aren’t in this list will have to be interpreted, which could affect runtime performance.

BoardConfig.mk:

WITH_DEXPREOPT := true
WITH_DEXPREOPT_BOOT_IMG_ONLY := true

device.mk:

PRODUCT_COPY_FILES += <filename>:system/etc/compiled-classes

If a device has both limited space in the system and data partitions, compiler filter flags can be used to disable compilation of certain apps. This will save space in both system and data, as there won’t be any compiled code, but these apps will have to be interpreted. This example configuration would pre-optimize the boot classpath but prevent compilation of other apps that are not prebuilts. However, to prevent noticeable performance degradation of system_server, the services.jar is still compiled but optimized for space. Note that user-installed applications will still use the default compiler filter of speed.

BoardConfig.mk:

WITH_DEXPREOPT := true
DONT_DEXPREOPT_PREBUILTS := true

device.mk:

PRODUCT_DEX_PREOPT_DEFAULT_FLAGS := --compiler-filter=interpret-only
$(call add-product-dex-preopt-module-config,services,--compiler-filter=space)

For a major version upgrade OTA, it can be useful to blacklist certain apps from being pre-optimized since they will likely be out of date. This can be done by specifying LOCAL_DEX_PREOPT (for all products) or with PRODUCT_DEX_PREOPT_MODULE_CONFIGS (for a particular product).

BoardConfig.mk:

WITH_DEXPREOPT := true

Android.mk (of blacklisted apps):

LOCAL_DEX_PREOPT := false



参考 2:

Android ART虚拟机中 boot.art 和 boot.oat 之间什么关系?