那chrome是怎么做到的呢?
为了性能,或者资源,程序中无法立即用到的对象,我们无需立即创造,比如一个界面的菜单对象,当你还没有点击菜单之前就创造了,那他一定会影响软件的启动性能。
再比如数据库对象,或者线程...等等,进可能延迟,chrome高效的原因之一,在于调用无数个LazyInstance对象。
研究过chrome的同学应该知道,chrome的管理任务的线程模型,他的所有线程(除了主线程),都可以延迟加载的。
作为延迟加载模版类LazyInstance<Type, Traits>它具备哪些特点呢?
1.线程安全。
2.LazyInstance不像单实例模型Singleton,他可以支持一个类型的多个实例。
3,为LazyInstance的类型预先分配空间,减少堆空间产生碎片。
4.优美的删除
使用方法:
- static LazyInstance<MyClass> my_instance(base::LINKER_INITIALIZED);
- void SomeMethod() {
- my_instance.Get().SomeMethod(); // MyClass::SomeMethod()
- MyClass* ptr = my_instance.Pointer();
- ptr->DoDoDo(); // MyClass::DoDoDo
- }
从上可知:
- LazyInstance<T>::Get()返回的是对象引用
- LazyInstance<T>::Pointer()返回的是对象指针
接下来我们一一说明一下上面所述的特点是怎么做到的。
1.线程安全
为了线程安全LazyInstance声明了三个标记:
enum {
STATE_EMPTY = 0, //没有创建实例
STATE_CREATING = 1,//正在创建实例,这时另一个被创建的例程应该先被Hold住,第一个先创建完后返回
STATE_CREATED = 2//已经创建了,不需要重复创造。
};
延迟加载当然是第一次调用Pointer()方法时创建(对于Get()方法,当然里面会调用Pointer方法来实现,尽可能重用代码).
1).Pointer方法首先不用加锁,快速获取state_变量是否已经变成STATE_CREATED状态,如果已经创建了,就直接返回,否则通过加多线程锁进行check一下(调用NeedInstance方法),是否是正在创建状态,如果正在被另一个线程创建,则等待他创建完然后直接返回指针。
这里LazyInstance尽可能地避免了加锁,只要创建完了一个实例后,后续的所有对状态的判断都不会涉及加锁。
2).如果需要创建则进行调用
- Traits::New
Chrome提供了默认的Trait以供通用。如果特意为某个类进行声明new方法,则定义Trait类,并对其New里面定义相关对象申请方法,然后在LazyInstance第二个模板里传进去
3).返回指针
- Type* Pointer() {
- if (!Traits::kAllowedToAccessOnNonjoinableThread)
- base::ThreadRestrictions::AssertSingletonAllowed();
- if ((base::subtle::Acquire_Load(&state_) != STATE_CREATED) &&
- NeedsInstance()) {
- instance_ = Traits::New(buf_);
- void (*dtor)(void*) = Traits::Delete;
- CompleteInstance(this, (dtor == NULL) ? NULL : OnExit);
- }
- ANNOTATE_HAPPENS_AFTER(&state_);
- return instance_;
- }
2.LazyInstance不像单实例模型Singleton,他可以支持一个类型的多个实例。
通过上面实力可以看出,对每个延迟加载对象进行全局定义即可。
3,为LazyInstance的类型预先分配空间,减少堆空间产生碎片。
含有成员变量预先为特定类进行分配空间,如下:
int8 buf_[sizeof(Type)];
这样调用LazyInstance的时候我们用的都是静态全局变量,这些都会存放在静态数据区域,而不是堆区域。对于堆、栈,静态区的一些介绍,参考:http://www.cnblogs.com/avril/archive/2011/04/28/2031886.html
然后通过调用Trait里New方法,POD( what is POD:http://en.wikipedia.org/wiki/Plain_old_data_structure )类型初始化
如下:
template <typename Type>
struct DefaultLazyInstanceTraits {
...
static Type* New(void* instance) {
return new (instance) Type();
}
...
}
4.优美的删除
删除当然也是调用Trait的Delete方法,当然你也可以指定自己定义的Delete方法。
那Delete是什么时候被调用呢?
Chrome提供了另一个类AtExitManager来管理全局对象,当整个程序结束时,AtExitManager会调用析构函数,此时会调用注册在AtExitManager里的所有回调函数。
当创建完LazyInstance对象后,在上面的CompleteInstance方法里注册Delete到AtExitManager实例即可显示释放内存,避免泄露,如下:
- void LazyInstanceHelper::CompleteInstance(void* instance, void (*dtor)(void*)) {
- base::subtle::Release_Store(&state_, STATE_CREATED);
- if (dtor)
- base::AtExitManager::RegisterCallback(dtor, instance);
- }
总结:
通过分析LazyInsance我们可以知道,Chrome是如何优化性能的。1.尽可能的减少锁的范围。
2.尽可能延迟加载。
3.尽可能减少内存碎片。