您的位置:首页 > 运维架构

ART深入浅出2 -- 认识和了解Runtime Options

2017-07-14 20:51 423 查看
本文基于Android 7.1,不过因为从BSP拿到的版本略有区别,所以本文提到的源码未必与读者找到的源码完全一致。本文在提供源码片断时,将按照
<源码相对android工程的路径>:<行号> <类名> <函数名> 的方式,如果行号对不上,请参考类名和函数名来找到对应的源码。

RuntimeOptions到RuntimeArgumentMap

在JNI_CreateJavaVM函数中,JavaVMOption对象被转换为RuntimeOptions对象。
RuntimeOptions实际上只是一个vector定义:
art/runtime/parsed_options.h:38
typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions;

内部的parie对象,第一个参数保存的是放进去的字符串数据,第二个是附加参数。一般附加参数不用。

art/runtime/java_vm_ext.cc:947 JNI_CreateJavaVM
for (int i = 0; i < args->nOptions; ++i) {
JavaVMOption* option = &args->options[i];
options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));
}
当数据填充完成后,调用Runtime::Create方法,传递给Runtime,用于创建Runtime。
art/runtime/runtime.cc:486 Runtime::Create
bool Runtime::Create(const RuntimeOptions& raw_options, bool ignore_unrecognized) {
RuntimeArgumentMap runtime_options;
return ParseOptions(raw_options, ignore_unrecognized, &runtime_options) &&
Create(std::move(runtime_options));
}
注意到return语句,先调用ParseOptions, 将RuntimeOptions转换为RuntimeArugmentMap。
本章要重点分析的就是这个ParseOptions函数。
首先看看RuntimeArugmentMap是什么结构。

Runtime::ParseOptions

该函数解析参数的入口。Runtime::ParseOptions调用 ParsedOptions:Parse函数,再调用 ParsedOptions::DoParse函数。
DoParse函数,主体有两个函数:MakeParse和RuntimeParser::Parse函数。

首先,看看MakeParse函数。

MakeParse函数

这个函数用了非常复杂的C++模板,生成一个解析数据结构。因为内容太多,我摘录了关键部分,进行解说。
请先看下面的代码
runtime/parsed_options.cc:61 ParsedOptions::MakeParser
std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognized) {
using M = RuntimeArgumentMap;

std::unique_ptr<RuntimeParser::Builder> parser_builder =
std::unique_ptr<RuntimeParser::Builder>(new RuntimeParser::Builder());

parser_builder->
Define("-Xzygote")
.IntoKey(M::Zygote)
.Define("-help")
.IntoKey(M::Help)
.....
.WithType<ParseStringList<':'>>()  // std::vector<std::string>, split by :
.IntoKey(M::BootClassPathLocations)
.Define({"-classpath _", "-cp _"})
.WithType<std::string>()
.IntoKey(M::ClassPath)
.Define("-Ximage:_")
.WithType<std::string>()
.IntoKey(M::Image)
....
      .Define("-D_")
          .WithType<std::vector<std::string>>().AppendValues()
          .IntoKey(M::PropertiesList)
....
      .Define({"-XX:EnableHSpaceCompactForOOM", "-XX:DisableHSpaceCompactForOOM"})
          .WithValues({true, false})
          .IntoKey(M::EnableHSpaceCompactForOOM)
....
      .Define("-XX:HeapTargetUtilization=_")
          .WithType<double>().WithRange(0.1, 0.9)
....
      .Define("-Xint")
          .WithValue(true)
          .IntoKey(M::Interpret)
....
.Define("-Xmx_")
.WithType<MemoryKiB>()
.IntoKey(M::MemoryMaximumSize)
.....
.Define("-XX:DumpNativeStackOnSigQuit:_")
.WithType<bool>()
.WithValueMap({{"false", false}, {"true", true}})
.IntoKey(M::DumpNativeStackOnSigQuit)
.....
.Define("-XX:LargeObjectSpace=_")
.WithType<gc::space::LargeObjectSpaceType>()
.WithValueMap({{"disabled", gc::space::LargeObjectSpaceType::kDisabled},
{"freelist", gc::space::LargeObjectSpaceType::kFreeList},
{"map",      gc::space::LargeObjectSpaceType::kMap}})
.IntoKey(M::LargeObjectSpace)
.....
.Ignore({
"-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
.....
"-Xjitdisableopt", "-Xjitsuspendpoll", "-XX:mainThreadStackSize=_"})
.IgnoreUnrecognized(ignore_unrecognized);

// TODO: Move Usage information into this DSL.

return std::unique_ptr<RuntimeParser>(new RuntimeParser(parser_builder->Build()));
}


该函数用了一些模板函数,创建了一个解析器。Define函数创建一个特定解析器,特殊符号'_'表示匹配剩余部分字符串。 WithType函数指定'_'匹配部分需要转换为的对象;WithValueMap函数定义了字符串与最终值之间的映射关系。最后IntoKey函数建立和一个key的关联。
这样,解析完成后,结果就存储在 RuntimeArgumentMap对象中,而key正是IntoKey指定的。

在理解之前,先了解几个类:

CmdlineParser::Builder   创建解析器的builder
UntypedArgumentBuilder  创建一个没有指定类型的参数Builder
template <typename TArg>  ArgumentBuilder 创建一个指定类型的参数builder
首先,Builder.Define函数创建一个UntypedArgumentBuilder实例
其次,UntypedArgumentBuilder.WithType函数创建一个ArgumentBuilder实例
第三,为ArgumentBuilder添加一系列的解析信息
最后,调用ArugmentBuilder.IntoKey,返回之前的Builder。
这里需要注意的是,UntypedArgumentBuilder和ArugmentBuilder其实都是临时对象,而Builder的实例则是一直存在的。
这样完成一个链式调用,可以方便的增加若干各模块。

ArgumentBuilder类支持若干种解析方法:

WithRange, 针对数值类型,指出数值的值域
WithValue: 如果一个参数被设置,就设置一个对应的值,否则就不设置。比如Define("-Xint").WithValue(true) 。 这里因为已经给出了具体value值,所以它的type可以确定,也就无需先用WithType产生一个ArgumentBuilder了,UntypedArgumentBuilder就能做到;
AppendValues 将多个参数的值收集到一个key下面。比如.Define("-D_").WithType<std::vector<std::string>>().AppendValues() 把多个-D定义的信息都放在一个模块内部。可以看到,这个属性是用vector数组保存的对象。
WithValues: 同WithValue,只是接受一个数组,建立一个映射值,例如.Define({"-XX:EnableHSpaceCompactForOOM", "-XX:DisableHSpaceCompactForOOM"})   .WithValues({true, false}) 如何设置第一个参数-XX:EnableHSpaceCompactForOOM,则取true,否则取false
WithValueMap: 将取得的值与最终建立一个映射,如 

Define("-Xprofile:_")
.WithType<TraceClockSource>()
.WithValueMap({{"threadcpuclock", TraceClockSource::kThreadCpu},
{"wallclock",      TraceClockSource::kWall},
{"dualclock",      TraceClockSource::kDual}})
                "_"所匹配的值,将和map列出的值一一对应。

到目前为止,就可以了解参数解析的基本用法,并且能够修改参数了。如果您不想了解它的工作原理,您可以结束本章阅读了。如果本着死磕精神,我们就深入了解下,这种神奇的用法是如何用模板实现的。

Parse的基本原理讲解

在正式了解Parse的实现过程之前,我们先想想应该怎么实现。
根据MakeParser函数的写法,我们可以推测出开发者的意图是这样的:

建立一个解析器数组,数组内的每个解析器,都解析一个或者多个关联的参数
解析器有一个匹配器,通过字符串比较,找出要解析的参数,很多情况下,要解析参数前面的名称部分
解析器要获取参数值部分,并把参数值转换为各种内部数据结构
参数解析器还带有一个Key,将此Key和最终结果,关联存储到一个Map表中
内部的数据结构包括:
true/false
数值,可能带有值域
枚举值
字符串列表
带单位的数值,比如最大/最小堆大小,以MB为单位

一个Map表存储Key-Value值

OK,现在,我们试着用伪代码来表示这个想法,现在定义各个结构的名称:

CmdlineParser: 管理解析器数组的类,里面有一个参数解析器数组 argument_parsers_
ArgumentParser: 参数解析器类, 有函数:
match: 进行参数匹配; 
fetchValue:从参数中提取值
covertValue:将参数值转为内部程序可以直接读取的值
key:得到关联的访问key

OptionsMap 保存结果的表,将key和value保存起来

参数列表是一个std::vector<string> arguments对象,那么伪代码可以这么写
CmdlineParser parser; //解析对象
OptionsMap    optionsMap; //保存结果
for (auto it = arguments.begin(); it != argments.end(); ++it) {
stirng arg = *it;
for (auto it_parser = parser.argument_parsers_.begin(); it_parser != parser.argument_parsers_.end(); ++it_parser) {
if (it_parser->match(arg)) {
string str_value = it_parser->fetchValue();
auto value = it_parser->covertValue(str_value);
optionsMap.put(it_parser->key(), value);
}
}
}


下面,我们就一一的对应ART中的源码,看看这些想法究竟是怎么实现的?

CmdlineParser

MakeParser函数最后返回的是一个RuntimeParser的指针对象,RuntimeParser就是CmdlineParser对象:
art/runtime/parsed_options.cc:53
using RuntimeParser = CmdlineParser<RuntimeArgumentMap, RuntimeArgumentMap::Key>;
其中,RuntimeArgumentMap就是上面说的OptionsMap对象,RuntimeArgumentMap::Key是和它对应的key对象。
查看CmdlineParser类的定义,发现它的成员有
art/cmdline/cmdline_parser.h:41
template <typename TVariantMap,
template <typename TKeyValue> class TVariantMapKey>
struct CmdlineParser {
.... //604
private:
bool ignore_unrecognized_ = false;
std::vector<const char*> ignore_list_;
std::shared_ptr<SaveDestination> save_destination_;
std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
};
这个类的定义很长,有600多行,但是,大部分是内部类的定义和实现函数,它的核心数据就是上面这些。
其中ignore_list_就是忽略的参数列表,这个没什么。
save_destination_,这是用来想OptionsMap存储数据的,我们也不管它。
completed_aruments_ 就是最重要的参数解析器的列表。
看到,这个列表是CmdlineParseArgumentAny对象的指针数组,为什么是指针?因为需要处理很多种不同的数据类型,只能通过虚函数来实现,所以要做成指针数组,而不能是对象数组。

TokeRange

首先介绍下TokeRange这个类。这个类辅助比较用的。因为下面进行比较时,会大量用到这个类,所以有必要先介绍它。
TokenRange类,先看看它的核心函数和成员
art/cmdline/token_range.h:35
struct TokenRange {
// Short-hand for a vector of strings. A single string and a token is synonymous.
using TokenList = std::vector<std::string>;
.... //329

// Do a quick match token-by-token, and see if they match.
// Any tokens with a wildcard in them are only matched up until the wildcard.
// If this is true, then the wildcard matching later on can still fail, so this is not
// a guarantee that the argument is correct, it's more of a strong hint that the
// user-provided input *probably* was trying to match this argument.
//
// Returns how many tokens were either matched (or ignored because there was a
// wildcard present). 0 means no match. If the size() tokens are returned.
size_t MaybeMatches(const TokenRange& token_list, const std::string& wildcard) const {
....

private:
....//418
const std::shared_ptr<std::vector<std::string>> token_list_;
const iterator begin_;
const iterator end_;
};
token_list_其实就是一个 字符串数组,也就是arguments数组。begin_和end_保存是这个数组的开头和结尾,也就是说,TokenRange保存的是整个参数数组的一部分

因为参数解析器可能会同时解析几种参数,有几种情况:

参数缩写,比如-classpath和-cp cp是classpath的缩写,两者都要识别
互斥参数,比如-XX:EnableHSpaceCompactForOOM和-XX:DisableHSpaceCompactForOOM这种带有enable/dispable的参数

MaybeMatches函数返回的是有多少个匹配的token。因为包含了匹配字符"_",所以,有时候会出现匹配不精确的问题,比如 "-XAB_"和"-XABC_"这两种token都能匹配参数” -XABCDEF“,但是按照精确的原则,应该是匹配 "-XABC_"而不是"-XAB_"。

注:上面的说法是我按照注释,加上自己的猜想。但是根据MaybeMatches函数的实现,我感觉与注释的行为并不一致。开发者可能自己也糊涂了。实际上,java vm参数设计上不会出现上面举例的情况,反倒是开发者自己想多了。有兴趣的读者可以自行看代码了解。

CmdlineParseArgumentAny,template <typename TArg> CmdlineParseArgument

CmdlineParseArgumentAny是一个纯虚类,只是定义了解析器的接口。

关键的接口是:

MaybeMatches : 比较match的接口
ParseArgument : 提取参数并转换的函数

它的实现类就是 CmdlineParseArgument,这个类是一个模板类,必须指定具体类型才能实例化。
现在看看CmdlineParseArgument的实现。

CmdlineParseArgumentInfo类

CmdlineParseArgument的很多信息都是放在这个类中的,所有有必要先了解这个类的结构。
代码在art/cmdline/detail/cmdline_parse_argument_detail.h:88,不在列代码了,只列出关键的成员

std::vector<const char*> names_ :  这是Builder.Define函数给出的列表,用于做参数匹配等操作
bool using_blanks_:  names_里面是否至少有一个name包含了 "_", 有则true,否则为false
std::vector<TokenRange> tokenized_names_ 将names_ tokenized化后的结果,方便match
std::vector<TokenRange> simple_names_  除去掉"_"字符所有名字
appending_values_: 如果调用了AppendValues就会为true。这样会将所有的值放在同一key下
has_range_, TArg min_, max_; 调用了WithRange后,就会设置为true,并填充min,max的值
has_value_map_, std::vector<std::pair<const char*, TArg>> value_map_ 调用WithValueMap就会使用它们
has_value_list_, std::vector<TArg> value_list_,调用WithValues就会使用它们。主要value_list_和name_它们是用索引一一对应的,比如names_[0]就对应value_list_[0]

成员函数MaybeMatches用于实现对参数的匹配。

CmdlineParseArgument.MaybeMatches

见CmdlineParseArgumentInfo.MaybeMatches的实现。

CmdlineParseArgument.ParseArgument

该函数返回CmdlineResult对象,表示成功或者失败。如果失败了,这个对象内包含有失败的描述信息。
这个函数有两部分组成:1. 解析参数, 2. 转换值,由函数ParseArgumentSingle来实现。

参数解析的过程是这样的:
如果using_blanking为false,那么,直接调用ParseArgumentSingle,走value_list_的映射处理
否则,提取出"_"映射的部分,比如"foo:bar",提取出blank_value == "bar",然后调用ParseArgumentSingle

CmdlineParseArgument.ParseArgumentSingle

这个函数的实现看似很长,实际上,可以分为好几种情况进行处理。最终是调用SaveArgument函数,将值存入到OptionsMap中,这个函数就完成任务了。

hash_value_map_ == true

从value_map_中映射取得值
art/cmdline/detail/cmdline_parse_argument_detail.h:386 CmdlineParseArgument.ParseeArgumentSingle

if (argument_info_.has_value_map_) {
for (auto&& value_pair : argument_info_.value_map_) {
const char* name = value_pair.first;

if (argument == name) {
return SaveArgument(value_pair.second);
}
}
.......
}


has_value_list_ == true

从names_和value_list_中取得值
art/cmdline/detail/cmdline_parse_argument_detail.h:409 CmdlineParseArgument.ParseeArgumentSingle

if (argument_info_.has_value_list_) {
size_t arg_def_idx = 0;
for (auto&& value : argument_info_.value_list_) {
auto&& arg_def_token = argument_info_.names_[arg_def_idx];

if (arg_def_token == argument) {
return SaveArgument(value);
}
++arg_def_idx;
}

....
}


appending_values_ == true

这有两个步骤:
1. 调用load_argument_方法(这是一个重载了operator()的类对象),获取已经加入的值,或者是新建一个值来加入
2. 调用type_parser.ParseAndAppend方法把值解析并加入进去
art/cmdline/detail/cmdline_parse_argument_detail.h:438 CmdlineParseArgument.ParseeArgumentSingle

if (argument_info_.appending_values_) {
TArg& existing = load_argument_();
CmdlineParseResult<TArg> result = type_parser.ParseAndAppend(argument, existing);

assert(!argument_info_.has_range_);

return result;
}


最后

调用type_parser.Parse方法直接进行解析
art/cmdline/detail/cmdline_parse_argument_detail.h:447 CmdlineParseArgument.ParseeArgumentSingle

CmdlineParseResult<TArg> result = type_parser.Parse(argument);


type_parser

type_parser这变量的类型,是UserTypeInfo,即
art/cmdline/detail/cmdline_parse_argument_detail.h:311 CmdlineParseArgument

using UserTypeInfo = CmdlineType<TArg>;
CmdLineType的定义是一个空的,但是,它有若干个半实例化的类型定义:
art/cmdline/cmdline_types.h:42
template <typename T>
struct CmdlineType : CmdlineTypeParser<T> {
};

// Specializations for CmdlineType<T> follow:

// Parse argument definitions for Unit-typed arguments.
template <>
struct CmdlineType<Unit> : CmdlineTypeParser<Unit> {
Result Parse(const std::string& args) {
if (args == "") {
return Result::Success(Unit{});  // NOLINT [whitespace/braces] [5]
}
return Result::Failure("Unexpected extra characters " + args);
}
};

template <>
struct CmdlineType<JDWP::JdwpOptions> : CmdlineTypeParser<JDWP::JdwpOptions> {
...
Result Parse(const std::string& options) {
....
}

Result ParseJdwpOption(const std::string& name, const std::string& value,
JDWP::JdwpOptions* jdwp_options) {
....
}

static const char* Name() { return "JdwpOptions"; }
};

template <size_t Divisor>
struct CmdlineType<Memory<Divisor>> : CmdlineTypeParser<Memory<Divisor>> {
using typename CmdlineTypeParser<Memory<Divisor>>::Result;

Result Parse(const std::string arg) {
....
}

....
};

template <>
struct CmdlineType<double> : CmdlineTypeParser<double> {
Result Parse(const std::string& str) {
....
}

static const char* Name() { return "double"; }
};

template <>
struct CmdlineType<unsigned int> : CmdlineTypeParser<unsigned int> {
Result Parse(const std::string& str) {
....
}

static const char* Name() { return "unsigned integer"; }
};

template <>
struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std::string>> {
  Result Parse(const std::string& args) {
    ...
  }

  Result ParseAndAppend(const std::string& args,
                        std::vector<std::string>& existing_value) {
...
  }

  static const char* Name() { return "std::vector<std::string>"; }
};

.....
template <>
struct CmdlineType<XGcOption> : CmdlineTypeParser<XGcOption> {
  Result Parse(const std::string& option) {  // -Xgc: already stripped
    .....
  }

  static const char* Name() { return "XgcOption"; }
};
....

template<>
struct CmdlineType<BackgroundGcOption>
  : CmdlineTypeParser<BackgroundGcOption>, private BackgroundGcOption {
  Result Parse(const std::string& substring) {
....
  }

  static const char* Name() { return "BackgroundGcOption"; }
};

template <>
struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> {
  Result Parse(const std::string& options) {
.....
  }

  static const char* Name() { return "LogVerbosity"; }
};

template <>
struct CmdlineType<TestProfilerOptions> : CmdlineTypeParser<TestProfilerOptions> {
 .....
 public:
  Result ParseAndAppend(const std::string& option, TestProfilerOptions& existing) {
   ....
  }

  static const char* Name() { return "TestProfilerOptions"; }
  static constexpr bool kCanParseBlankless = true;
};

template<>
struct CmdlineType<ExperimentalFlags> : CmdlineTypeParser<ExperimentalFlags> {
  Result ParseAndAppend(const std::string& option, ExperimentalFlags& existing) {
 ....
  }

  static const char* Name() { return "ExperimentalFlags"; }
};


当在WithType的时候给出什么参数, C++就会在这里挑选一个备用的半实例化CmdlineType类,而这个CmdlineType就知道如何解析该类型的参数了。
我没有列出具体的解析代码,是因为这些不重要,重要的是了解到它们的实现机理。

注意到,半实例化的CmdlineType都继承了CmdlineTypeParser类。这是因为,CmdlineType必须声明两个函数Parser和ParseAndAppend。但是,很多情况下,是不需要ParseAndAppend函数的。ART为了方便,就声明了一个CmdlineTypeParser类,大家都继承它。这样,如果需要ParseAndAppend函数时,就定义它,不需要时,就用基础类CmdlineTypeParser的,避免了重复声明。

当我们在using UserTypeInfo = CmdlineType<TArg>; 时候,C++编译器就根据TArg就选好了具体实现的CmdlineType了。因此,直接调用它的对象的Parser或者ParseAndAppend函数即可。

至于UntypedArgumentBuilder.WithType函数,只是创建了一个ArgumentBuilder类实例,只要把TArg参数传递出去,解析器就可以自动选定了。

RuntimeArgumentMap

最后来说说这个Map对象。RuntimeArgumentMap定义来自
art/runtime/runtime_options.h:50
template <typename TValue>
struct RuntimeArgumentMapKey : VariantMapKey<TValue> {
RuntimeArgumentMapKey() {}
explicit RuntimeArgumentMapKey(TValue default_value)
: VariantMapKey<TValue>(std::move(default_value)) {}
// Don't ODR-use constexpr default values, which means that Struct::Fields
// that are declared 'static constexpr T Name = Value' don't need to have a matching definition.
};

......
struct RuntimeArgumentMap : VariantMap<RuntimeArgumentMap, RuntimeArgumentMapKey> {
// This 'using' line is necessary to inherit the variadic constructor.
using VariantMap<RuntimeArgumentMap, RuntimeArgumentMapKey>::VariantMap;

// Make the next many usages of Key slightly shorter to type.
template <typename TValue>
using Key = RuntimeArgumentMapKey<TValue>;

// List of key declarations, shorthand for 'static const Key<T> Name'
#define RUNTIME_OPTIONS_KEY(Type, Name, ...) static const Key<Type> Name;
#include "runtime_options.def"
};


要读懂这个代码,需要一些技巧。
首先,我们看到有Variant这个关键字:VariantMap, VariantMapKey,这说明这个Map是可以存储任意类型的数据的。
其次,按照我们一般的观念,要存储任意类型,只有两种方法:1. 用union,2: 用void*只保存其指针,那么,可以推定VariantMap的value只能是二者之一
第三,不管是union还是void*,都必须知道数据的类型,否则数据无法访问,那么VariantMapKey<TValue>的模板参数TValue肯定是用来指出参数类型的;
第四,如果是作为key,那么只能是一个变量值,不能是一个类型。那么RUNTIME_OPTION_KEY宏显然是定义了一个静态key变量。显然 runtime_options.def文件里面,就是用宏RUNTIME_OPTION_KEY定义了一堆key。之所以用宏+runtime_options.def的方式定义,只是因为,在头文件中声明后,还必须在源文件中定义。可以参阅
art/runtime/runtime_options.cc:32

#define RUNTIME_OPTIONS_KEY(Type, Name, ...) const RuntimeArgumentMap::Key<Type> RuntimeArgumentMap::Name {__VA_ARGS__};  // NOLINT [readability/braces] [4]
#include "runtime_options.def"
展开之后,就是key的定义了。

VariantMapKey类的定义,大家可以自行看源码(art/runtime/base/variant_map.h), 它的作用,就是将所有对TValue类型的数据进行操作:创建、拷贝、删除等。不再赘述。

然后我们看看VariantMap类的实现。这里,关键是找它存储成员 StorageMap storage_map_, 而这个StoreageMap的定义就是:
art/runtime/base/variant_map.h:402
using StorageMap = std::map<const detail::VariantMapKeyRaw*, void*, KeyComparator>;
现在清楚了,它正是用void*存储数据的。

写在最后的话

本来,参数解析即算不上什么重要的模块,也算不上什么高深技术,即便一个菜鸟,用if-else结构,也完全能够写出解析器,无非看起来很low而已。稍微有点经验的,也可以利用虚函数,写出一个结构不错、也容易维护解析器来。ART没有这样做,而是利用模板,写了一个很漂亮、很高深的解析器。这个解析器无疑要比其他类型的解析器运行速度要快,至于快多少,除非写一个“常规”的解析器,才能比较出来。

实际上,这是我读到的ART源码中最复杂的一块了,激起我下决心把它搞清楚。我觉得,相对于这部分代码的本身意义,解读的方法,更值得向大家推荐:

先搞明白它的意图,要实现什么功能,达到什么效果
想一个一般的实现思路
按照这个思路,去源码中寻找实现这个思路的关键点,在这个过程中,成员变量比成员函数更重要,数据比过程更重要
迭代这个过程,从高层函数到底层函数,层层推进,就能摸清出它的实现方法。

最后,我给ART用模板的方法实现这个功能,给一个中肯的评价:反人类!我曾经有段时间很迷恋模板,甚至研究了boost的实现,用模板实现了简单的lambda表达式。最顶峰时,用模板写了一个ARM机器码反汇编器。但是后来发现,几乎没有人能够再看懂这些代码,即便我自己也需要花些时间来温习。既然如此,用模板还有什么意义?一个设计良好的虚拟类+简单的模板运用,是不是比这样做更清晰、更容易理解、更容易维护呢?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: