allocated

更新时间:2023-04-13 18:25:55 阅读: 评论:0


2023年4月13日发(作者:adverbial)

OC底层原理03:内存对齐原理和malloc源码分析

获取内存⼤⼩的三种⽅式

sizeof

:这是⼀个

操作符

,课操作的数据有

基本数据类型

指针

对象

。由于它不是函数,所以在

编译时就确定了⼤⼩。

class_getInstanceSize:获取

实例变量的成员变量⼤⼩之和

8

字节对齐,⽤之前需要导⼊头⽂件

objc/runtime.h

malloc_size:

获取系统实际开辟的内存⼤⼩

,⽤之前需导⼊头⽂件

malloc/malloc.h。

结构体内存对齐

⾸先⾃定义两个结构体,打印两个结构体的内存⼤⼩。

打印struct内存⼤⼩

从上图可以看到两个结构体的成员变量⼀样,但是由于

定义的顺序不⼀样

,导致了最后的

内存⼤⼩也不⼀样

。这种现象就是

iOS

中的内存字节对

齐。

根据上篇⽂章的内存对齐原则来⼿动计算⼀下两个结构体的内存⼤⼩。StructA:

a是⼀个

char

类型,占

1个字节。

b是⼀个

double

类型,占

8

个字节。并且要从

8

的整数倍开始存取,所以a后⾯要补上

7

个字节的空数据。

c是⼀个

short

类型,占

2个字节。

d是⼀个

int

类型,占

4

个字节。并且要从

4

的整数倍开始存取,所以c后⾯要补上

2

个字节的空数据。

所以StructA总的⼤⼩为:((1(a)+7(空数据)+8(b)+2(c)+2(空数据)+4(d)+7)>>3<<3=24StructB:

b是⼀个

double

类型,占

8

个字节。

d大骨汤面 是⼀个

int

类型,占

4

个字节。

c是⼀个

short

类型,占

2

个字节。

d是⼀个

char

类型,占

1个字节。

所以StructB总的⼤⼩为:((8(b)+4(d)+2(c)+1(d)+7)>>3<<3=16再来探索⼀下结构体嵌套的情况。

结构体嵌套

⼿动计算⼀下StructC的内存⼤⼩。

a是⼀个

char

类型,占

1个字节。

b是⼀个

double

类型,占

8

个字节。并且要从

8

的整数倍开始存取,所以a后⾯要补上

7

个字节的空数据。

c是⼀个

short

类型,占

2个字节。

structA是⼀个

StructA

类型,前⾯已经算出为

24

个字节。并且要从StructA内部最⼤的成员(

double8

字节)整数倍开始存取,所以c后⾯要补

6个字节空数据。

d是⼀个

int

类型,占

4个字节。

所以StructA总的⼤⼩为:((1(a)+7(空数据)+8(b)+2(c)+6(空数据)+24(structA)+4(d)+7)>>3<<3=56

所以当我们定义结构体的时候,为了优化内存,减少开销,可以将成员从⼤到⼩排列。下⾯是⼀些iOS中常见类型在32位和64位设备上对应的⼤⼩。

C

bool

signedcharunsignedchar

short

unsignedshor吃什么促排卵 tsignedshort

int

unsignedint

signedint

long

unsignedlonglonglong

OC

BOOL(32位)

SInt8、int8_tUInt8、Boolean

int16_t

UInt16SInt16

int32_t、NSInteger(32位)、boolean_t(32位)UInt32(64位)、boolean(64位)NSUInteger(32位)

SInt32(64位)

NSInteger(64位)NSUInteger(64位)

int64_t

32位

1

1

1

2

2

2

4

4

4

4

48

64位

1

1

1

2

2

2

4

4

4

8

88

unsignedlonglongCsignedlonglong

floatdouble

看完了结构体我们再来看看OC中对象的内存结构。

UInt64OC

SInt64

CGFloat(32位)CGFloat(64位)

32位8

8

48

64位8

8

48

@interfacePerson:NSObject

@property(nonatomic,copy)NSString*name;

@property(nonatomic,assign)unsignedshortage;

@property(nonatomic,copy)NSString*nickName;

@property(nonatomic,assign)doubleheight;

@property(nonatomic,assign)charx;

@property(nonatomic,assign)longscore;@end

person的内存结构

可以看到,OC中对象的内存结构跟定义的属性顺序⽆关。第1个8字节是isa指针。

第2个8字节存储的是age和x。

第3个和第4个存储的是name和nickName。

第5个字节存储的是height。第6个字节存储的是score。

由此可得出,OC对对象的内存结构进⾏了

重排

减少了内存开销

,使

读取速度变快

。额外补充⼀点⼉,Swift中由于是静态语⾔,所以并不会属性进⾏重排。

swift中对象的内存结构

可以看到Swift中的对象内存结构跟成员变量的顺序有关,所以为了节约内存提⾼读取速度,在Swift定义的模型类成员变量应该从⼤往⼩排列。

在上篇⽂章我们看到alloc底层调⽤了

calloc

开辟了⼀块内存,接下来探究

calloc

的底层。

准备⼯作:

下载malloc源码

编译源码,可参考这⾥

调⽤

calloc并打上断点。

调⽤calloc

通过

Stepinto

进⼊

calloc源码。

calloc

进来是中间层代码,继续跟进。

_malloc_zone_calloc

继续跟进。

default_zone_malloc

在这⼀步创建了真正的zone,然后⽤真正的zone进⾏calloc,继续跟进。

nano_call孔子登山打一字 oc

在这⼀步先计算了总的⼤⼩,然后根据⼤⼩进⼊不同的内存开辟⽅法,这⾥我是传⼊了40,⼩于

NANO_MAX_SIZE

(256),所以进⼊

_nano_malloc_check_clear。

_nano_malloc_check_clear

继续跟进

gregate_next_block。

gregate_next_block

如果是第⼀次调⽤

gregated_next_block

函数,

band

不存在,缓存也不会存在,所以会调⽤

gregated_band_grow

来开辟新的

band。

gregated_band_grow

关于通过

nano_blk_addr_t的联合体结构和宏定义如下。

structnano_blk_addr_s{

uint64_t

nano_offt:NANO_OFFSET_BITS,//17locatestheblock

nano_slot:NANO_SLOT_BITS,//4bucketofhomo恰似你的温柔歌词 genousquanta-multipleblocksnano_band:NANO_BAND_BITS,//17

nano_mag_index:NANO_MAG_BITS,//6thecorethatallocatedthisblock

nano_signature:NANOZONE_SIGNATURE_BITS;//theaddressrangedevotedtous.};

#endif

//clang-formatontypedefunion{

uint64_taddr;

structnano_blk_addr_sfields;}nano_blk_addr_t;

#defineSLOT_IN_BAND_SIZE(1<

#defineSLOT_KEY_LIMIT(1<

#defineBAND_SIZE(1<<(NANO_SLOT_BITS+NANO_OFFSET_BITS))/*==Numberofbytescoveredbyapagetableentry*/

#defineNANO_MAG_SIZE(1<

#defineNANO_SLOT_SIZE(1<

#defineNANO_MAG_BITS6

#defineNANO_BAND_BITS17

#defineNANO_SLOT_BITS4责任制度 #defineNANO_OFFSET_BITS17

下⾯来梳理下nanozone分配过程:

确定当前

cpu

对应的

mag

和通过

size

参数计算出来的

slot

,去对应

chained_block_s

的链表中取已经被释放过的内存区块缓存,如果取到检查指针地址是否有问题,没有问题就直接返回;

初次进⾏

nanomalloc

时,

nanozone

并没有缓存,会直接在

nanozone范围的地址空间上直接分配连续地址内存;

如当前

Band

中当前

Slot

耗尽则向系统申请新的

Band

(每个Band固定⼤⼩2M,容纳了16个128k的槽),连续地址分配内存的基地址、limit

地址以及当前分配到的地址由

metadata

结构维护起来,⽽这些

metadata

则以

Mag

Slot

为维度(Mag个数是处理器个数,Slot是16个)的⼆维

数组形式,放在

nanozone_t

meta_data字段中。

上⾯是当开辟的

size

⼩于

NANO_MAX_SIZE(256)

的情况,接下来探究当

size⼤于256的情况。

szone_calloc

继续跟进。

获取helper_zone

szone_calloc

nano_calloc

⼀样,先根据

num_items

计算

total_bytes,继续跟进。

szone_malloc_should_clear

这⾥以看出在

szone

上分配的内存根据

size

⼤⼩不同包括

tiny

small

medium

large

四⼤类。这⾥我传的

size

257

,所以会进⼊

tiny

⽀,我们以

tiny为例开始下⾯的分析。

void*

tiny_malloc_should_clear(rack_t*rack,msize_tmsize,boolean_tcleared_requested)

{

void*ptr;

//计算mag_index下标,magazines是⼀个由64个magazine_t组成的数组。

mag_index_tmag_index=tiny_mag_get_thread_index()%rack->num_magazines;

//根据mag_index下标获取magazine。

magazine_t*tiny_mag_ptr=&(rack->magazines[mag_index]);

MALLOC_TRACE(TRACE_tiny_malloc,(uintptr_t)rack,TINY_BYTES_FOR_MSIZE(msize),(uintptr_t)tiny_mag_ptr,cleared_requested);#ifDEBUG_MALLOC

if(DEPOT_MAGAZINE_INDEX==mag_index){

malloc_zone_error(rack->debug_flags,true,"malloccalledformagazineindex-1n");

return(NULL);}

if(!msize){

malloc_zone_error(rack->debug_flags,true,"invariantbroken(!msize)inallocation(region)n");

return(NULL);

}#endif

SZONE_MAGAZINE_PTR_LOCK(tiny_mag_ptr);

//如果开启了tiny的缓存

#ifCONFIG_TINY_CACHE

ptr=tiny_mag_ptr->mag_last_free;

if(tiny_mag_ptr->mag_last_free_msize==msize){

//wehaveawinner

//优先查看上持续英文 次最后释放的区块是否和此托业考试官网 次请求的⼤⼩刚好相等(都是对齐之后的slot⼤⼩),如果是则直接返回。

tiny_mag_ptr->mag_last_free=NULL;

tiny_mag_ptr->mag_last_free_msize=0;

tiny_mag_ptr->mag_last_free_rgn=NULL;

SZONE_MAGAZINE_PTR_UNLOCK(tiny_mag_ptr);

CHECK(szone,__PRETTY_FUNCTION__);if(cleared_requested){

memt(ptr,0,TINY_BYTES_FOR_MSIZE(msize));}

#ifDEBUG_MALLOCif(LOG(szone,ptr)){

malloc_report(ASL_LEVEL_INFO,"intiny_malloc_should_clear(),tinycacheptr=%p,msize=%dn",ptr,msize);

}

#endif

returnptr;}

#endif/*CONFIG_TINY_CACHE*/

//没有开启了tiny的缓存while(1){

//先从freelist查找

ptr=tiny_malloc_from_free_list(rack,tiny_mag_ptr,mag_index,msize);if(ptr){

SZONE_MAGAZINE_PTR_UNLOCK(tiny_mag_ptr);CHECK(szone,__PRETTY_FUNCTION__);

if(cleared_requested){

memt(ptr,0,TINY_BYTES_FOR_MSIZE(msize));}

returnptr;}

#ifCONFIG_RECIRC_DEPOT

//从⼀个后备magazine中取出⼀个可⽤region,完整地拿过来放到当前magazine,再⾛⼀遍上⾯的步骤。

if(tiny_get_region_from_depot(rack,tiny_mag_ptr,mag_index,msize)){

//再次尝试从freelist中获取

ptr=tiny_malloc_from_free_list(rack,tiny_mag_ptr,mag_index,msize);if(ptr){

SZONE_MAGAZINE_PTR_UNLOCK(tiny_mag_ptr);CHECK(szone,__PRETTY_FUNCTION__);

if(cleared_requested){

memt(ptr,0,TINY_BYTES_FOR_MSIZE(msize));}

returnptr;}

}

}

#endif//CONFIG_RECIRC_DEPOT

//gion(heap)mustbeallocatedtosatisfythiscalltomalloc().

//Theallocation,anmmap()systemcall,willbeperformedoutsidethemagazinespinlocksbythefirst

//readts"alloc_underway"andentersacriticalction.

//Threadsarrivingherelaterareexcludedfromthecriticalction,yieldtheCPU,andthenretrythe

//ometimethemagazineisresupplied,theoriginalthreadleaveswithitsallocation,

//(!tiny_mag_ptr->alloc_underway){

//如果没有正在申请新的的regin操作,则进⾏申请操作void*fresh_region;

//timetocreateanewregion(dothisoutsidethemagazinelock)

//设置当前正在申请新的堆

tiny_mag_ptr->alloc_underway=TRUE;OSMemoryBarrier();

SZONE_MAGAZINE_PTR_UNLOCK(tiny_mag_ptr);

//申请新的堆

fresh_region=mvm_allocate_pages(TINY_REGION_SIZE,TINY_BLOCKS_ALIGN,

MALLOC_FIX_GUARD_PAGE_FLA显卡怎么超频 GS(rack->debug_flags),VM_MEMORY_MALLOC_TINY);

SZONE_MAGAZINE_PTR_LOCK(tiny_mag_ptr);//DTraceUSDTProbe

MAGMALLOC_ALLOCREGION(TINY_SZONE_FROM_RACK(rack),(int)mag_index,fresh_region,TINY_REGION_SIZE);if(!fresh_region){//outofmemory!

tiny_mag_ptr->alloc_underway=FALSE;OSMemoryBarrier();

SZONE_MAGAZINE_PTR_UNLOCK(tiny_mag_ptr);

returnNULL;}

region_t_cookie(®ION_COOKIE_FOR_TINY_REGION(fresh_region));

//从最近的⼀个region或者新申请的region中malloc

ptr=tiny_malloc_from_region_no_lock(rack,tiny_mag_ptr,mag_index,msize,fresh_region);

//wedon'tclearbecauthisfreshlyallocatedspaceispristine

tiny_mag_ptr->alloc_underway=FALSE;OSMemoryBarrier();

SZONE_MAGAZINE_PTR_UNLOCK(tiny_mag_ptr);

CHECK(szone,__PRETTY_FUNCTION__);returnptr;

}el{

SZONE_MAGAZINE_PTR_UNLOCK(tiny_mag_ptr);yield();

SZONE_MAGAZINE_PTR_LOCK(tiny_mag_ptr);}

}

/*NOTREACHED*/}

tiny_malloc_from_free_lis

t函数的作⽤是从

free_list中不断进⾏各种策略尝试。

free_list

流程仍然找不到可以使⽤内存,就会使⽤

tiny_get_region_from_depot。

每⼀个类型的

rack

指向的

magazines

,都会在下标为-1,

magazine_t

当做备⽤:

depot

,该⽅法的作⽤是从备⽤的

depot

查找出是否有满⾜条件

region

如果存在,更新

depot

region

的关联关系,然后在关联当前的

magazine_t

region,之后在再次重复free_list过程。

scalable_zone分配过程

⾸先检查指针指向地址是否有问题。

如果

lastfree

指针上没有挂载内存区块,则放到

lastfree上。

如果有

lastfree

,置换内存,并把

lastfree

原有内存区块挂载到

freelist

上(在挂载的

freelist

前,会先根据

region

位图检查前后区块是否能合并成更⼤区块,如果能会合并成⼀个)。

合并后所在的region如果空闲字节超过⼀定条件,则将把此

region

放到后备的

magazine中(-1)。

如果整个

region

都是空的,则直接还给系统内核。最后是⼀张⼤神画的流许多英语 程图。

calloc流程图


本文发布于:2023-04-13 18:25:55,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/90/93074.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

下一篇:glimp
标签:allocated
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图