内存管理---slab机制 销毁对象

Linux内核中将对象释放到slab中上层所用函数为kfree()或kmem_cache_free()。两个函数都会调用__cache_free()函数。

缓存回收对象基于以下原则

1.本地高速缓存的空间还可以容纳空闲对象,则直接将对象放回本地高速缓存

2.本地高速缓存的空间已满,则按batchcount的值将对象从本地高速缓存转移到slab中,转移是基于先进先出的原则的,也就是转移entry数组最前面的batchcount个空闲对象,因为这些对象在数组中存在的时间相对较长,不太可能仍然驻留在CPU高速缓存中

1,当本地CPU cache中空闲对象数小于规定上限时,只需将对象放入本地CPU cache中;

2,当local cache中对象过多(大于等于规定上限),需要释放一批对象到slab三链中。由函数cache_flusharray()实现。

1)如果三链中存在共享本地cache,那么首先选择释放到共享本地cache中,能释放多少是多少;

2)如果没有shared local cache,释放对象到slab三链中,实现函数为free_block()。对于free_block()函数,当三链中的空闲对象数过多时,销毁此cache。不然,添加此slab到空闲链表。因为在分配的时候我们看到将slab结构从cache链表中脱离了,在这里,根据page描述符的lru找到slab并将它添加到三链的空闲链表中。

*
 * Release an obj back to its cache. If the obj has a constructed state, it must
 * be in this state _before_ it is released.  Called with disabled ints.
 */
static inline void __cache_free(struct kmem_cache *cachep, void *objp,
    void *caller)
{
	struct array_cache *ac = cpu_cache_get(cachep); /* 获得本CPU的local cache */ 

	check_irq_off();
	kmemleak_free_recursive(objp, cachep->flags);
	objp = cache_free_debugcheck(cachep, objp, caller);

	kmemcheck_slab_free(cachep, objp, cachep->object_size);

	/*
	 * Skip calling cache_free_alien() when the platform is not numa.
	 * This will avoid cache misses that happen while accessing slabp (which
	 * is per page memory  reference) to get nodeid. Instead use a global
	 * variable to skip the call, which is mostly likely to be present in
	 * the cache.
	 */
	if (nr_online_nodes > 1 && cache_free_alien(cachep, objp))
		return;
/*如果本地高速缓存中的空闲对象小于空闲对象上限,则直接用entry中的元素记录对象的地址*/  
	if (likely(ac->avail < ac->limit)) {
		STATS_INC_FREEHIT(cachep);
	} else {
		STATS_INC_FREEMISS(cachep);/*否则将本地高速缓存中的空闲对象批量转移到slab中*/  
		cache_flusharray(cachep, ac);
	}

	ac_put_obj(cachep, ac, objp);//实际上执行ac->entry[ac->avail++] = objp;
}

/*local cache中对象过多,需要释放一批对象到slab三链中。*/

static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac)
{
	int batchcount;
	struct kmem_list3 *l3;
	int node = numa_node_id();

	batchcount = ac->batchcount; /* 每次释放batchcount个对象 */  
#if DEBUG
	BUG_ON(!batchcount || batchcount > ac->avail);
#endif
	check_irq_off();
	l3 = cachep->nodelists[node];
	spin_lock(&l3->list_lock);
	if (l3->shared) {/*如果开启了共享本地高速缓存*/
		/*获取共享的array_cache*/
		struct array_cache *shared_array = l3->shared; /* 如果存在shared local cache,将对象释放到其中 */ 
		/*计算共享本地高速缓存还可容纳的空闲对象数*/
		int max = shared_array->limit - shared_array->avail;
		if (max) {
			if (batchcount > max)
				batchcount = max;
			/*将batchcount个对象移到共享本地高速缓存中*/
			memcpy(&(shared_array->entry[shared_array->avail]),
			       ac->entry, sizeof(void *) * batchcount);
			shared_array->avail += batchcount;
			goto free_done;
		}
	}

	/*将本地高速缓存的前batchcount个对象放回slab*/ /* 无shared local cache,释放对象到slab三链中 */  
	free_block(cachep, ac->entry, batchcount, node);
free_done:
#if STATS
	{
		int i = 0;
		struct list_head *p;

		p = l3->slabs_free.next;
		while (p != &(l3->slabs_free)) {
			struct slab *slabp;

			slabp = list_entry(p, struct slab, list);
			BUG_ON(slabp->inuse);

			i++;
			p = p->next;
		}
		STATS_SET_FREEABLE(cachep, i);
	}
#endif
	spin_unlock(&l3->list_lock);
	ac->avail -= batchcount;/*刷新本地高速缓存的avail值*/
/* local cache前面有batchcount个空位,将后面的对象依次前移batchcount位 */  
	memmove(ac->entry, &(ac->entry[batchcount]), sizeof(void *)*ac->avail);
}
static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
		       int node)
{
	int i;
	struct kmem_list3 *l3;

	for (i = 0; i < nr_objects; i++) {
		void *objp = objpp[i];
		struct slab *slabp;

		/*通过对象的虚拟地址得到slab描述符*/
<span style="white-space:pre">		/* 通过虚拟地址得到page,再通过page得到slab */ </span>
		slabp = virt_to_slab(objp);

		/*获取kmem_list3*/
		l3 = cachep->nodelists[node];

		/*先将slab从所在链表中删除*/
		list_del(&slabp->list);
		check_spinlock_acquired_node(cachep, node);
		check_slabp(cachep, slabp);

		/*将一个对象放回slab上*/
		slab_put_obj(cachep, slabp, objp, node);
		STATS_DEC_ACTIVE(cachep);

		/*kmem_list3中的空闲对象数加1*/
		l3->free_objects++;
		check_slabp(cachep, slabp);

		/* fixup slab chains */
		/*slab的对象全部空闲*/
		if (slabp->inuse == 0) {
			/*如果空闲对象数大于了空闲对象上限*/
			if (l3->free_objects > l3->free_limit) {
				/*总空闲对象数减去一个slab的对象数*/
				l3->free_objects -= cachep->num;
				/* No need to drop any previously held
				 * lock here, even if we have a off-slab slab
				 * descriptor it is guaranteed to come from
				 * a different cache, refer to comments before
				 * alloc_slabmgmt.
				 */
				 /*销毁该slab*/
				slab_destroy(cachep, slabp);
			} else {
				/*将该slab添加到free链表*/
				list_add(&slabp->list, &l3->slabs_free);
			}
		} else {/*否则添加到partial链表*/
			/* Unconditionally move a slab to the end of the
			 * partial list on free - maximum time for the
			 * other objects to be freed, too.
			 */
			list_add_tail(&slabp->list, &l3->slabs_partial);
		}
	}
}

对象释放到其slab中

static void slab_put_obj(struct kmem_cache *cachep, struct slab *slabp,
				void *objp, int nodeid)
{ 	/* 获得对象在kmem_bufctl_t数组中的索引 */
	unsigned int objnr = obj_to_index(cachep, slabp, objp);

#if DEBUG
	/* Verify that the slab belongs to the intended node */
	WARN_ON(slabp->nodeid != nodeid);

	if (slab_bufctl(slabp)[objnr] + 1 <= SLAB_LIMIT + 1) {
		printk(KERN_ERR "slab: double free detected in cache "
				"'%s', objp %p\n", cachep->name, objp);
		BUG();
	}
#endif
	/*这两步相当于静态链表的插入操作*/
	/* 指向slab中原来的第一个空闲对象 */
	slab_bufctl(slabp)[objnr] = slabp->free;
	/* 释放的对象作为第一个空闲对象 */
	slabp->free = objnr;
	/* 已分配对象数减一 */
	slabp->inuse--;
}
/* 通过虚拟地址得到page,再通过page得到slab */
static inline struct slab *virt_to_slab(const void *obj)
{
	struct page *page = virt_to_head_page(obj);
	return page_get_slab(page);
}
时间: 2024-08-01 12:20:20

内存管理---slab机制 销毁对象的相关文章

内存管理 ---slab机制 分配对象

从一个缓存中分配对象总是遵循下面的原则: 1.本地高速缓存中是否有空闲对象,如果有的话则从其中获取对象, 2.如果本地高速缓存中没有对象,则从kmem_list3中的slab链表中寻找空闲对象并填充到本地高速缓存再分配: 3.如果所有的slab中都没有空闲对象了,那么就要创建新的slab,再分配 . 来自:http://blog.csdn.net/vanbreaker/article/details/7671211 Linux内核从slab中分配内存空间由kmalloc()或kmem_cache

JVM内存管理的机制

1.JVM内存管理的机制 内存空间划分为:Sun JDK在实现时遵照JVM规范,将内存空间划分为堆.JVM方法栈.方法区.本地方法栈.PC寄存器. 堆: 堆用于存储对象实例及数组值,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中对象所占用的内存由GC进行回收,在32位操作系统上最大为2GB,在64位操作系统上则没有限制,其大小可通过-Xms和-Xmx来控制,-Xms为JVM启动时申请的最小Heap内存,默认为物理内存的1/64但小于1GB:-Xmx为JVM可申请的最大He

Nginx之共享内存与slab机制

1. 共享内存 在 Nginx 里,一块完整的共享内存以结构体 ngx_shm_zone_t 来封装,如下: typedef struct ngx_shm_zone_s ngx_shm_zone_t; typedef ngx_int_t (*ngx_shm_zone_init_pt) (ngx_shm_zone_t *zone, void *data); typedef struct { /* 执行共享内存的起始地址 */ u_char *addr; /* 共享内存的长度 */ size_t s

Linux内存管理2---段机制

1.前言 本文所述关于内存管理的系列文章主要是对陈莉君老师所讲述的内存管理知识讲座的整理. 本讲座主要分三个主题展开对内存管理进行讲解:内存管理的硬件基础.虚拟地址空间的管理.物理地址空间的管理. 本文将主要以X86架构为例来介绍Linux内存管理的段机制. 2.段机制 段是虚拟地址空间的基本单位 段机制必须把虚拟地址空间的一个地址转换为线性地址空间的一个线性地址 可以从三个方面来描述段(如下图) 段的基地址(Base):在线性地址空间中段的起始地址: 段的界限(Limit):在虚拟地址空间中,

OC_内存管理(二)对象复制、循环引用问题、自动释放池

循环调用: 1.循环引用的问题 两个对象A.B,有可能会出现特殊情况:A中包含B的实例变量:B中也包含A的实例变量,如果这两个实例变量都是强引用(A有着B的实例变量所有权,B也有A的实例变量所有权),然后在两个对象销毁时,会出现A.B都不能正常销毁的情况.下面用代码来演示循环调用问题: ClassA的头文件Class.h代码 #import <Foundation/Foundation.h> //类的前向声明 @class ClassB; //类的前向声明与包含头文件的区别?Q: @inter

Android——内存管理-lowmemorykiller 机制

前段时间碰到一个apk多个process依次开跑,跑到最后一个process的时候,第一个process给kill掉了,虽然第一个process中含有broadcast receive,被kill掉的原因是由于触发到了lowmemorykiller,这样一来apk最后的结果就异常了~ 尝试再三 规避掉了这个问题,记录一下~ 撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/47317765本文来自 [jscese]的博客! 概念 a

黑马程序员--Objective-C 内存管理我之见解

------<a href="http://www.itheima.com" target="blank">Java培训.Android培训.iOS培训..Net培训</a>.期待与您交流! ------- 内存管理 为什么要进行内存管理? 因为设备的内存空间是有限的,如果一直占用,而不回收空间,内存就会被一直占用,导致内存不足,  系统就会就会报警,严重的可能直接退出程序,因此,在软件开发过程中,需要进行内存管理,以保证高效快速的分配内存,

Python内存管理机制

1 概述 对于Python这样的动态语言,如何高效的管理内存,是很重要的一部分,在很大程度上决定了Python的执行效率.与大多数编程语言不同,Python中的变量无需事先申明,变量无需指定类型,程序员无需关心内存管理,Python解释器给你自动回收.我们知道在变量分配内存时,是借用系统资源,在使用完成后,应该归还所借用的系统资源,Python承担了这个复杂的内存管理工作,从而让程序员更加的关注程序的编写质量上. 在执行过程中,Python会频繁的创建和销毁大量的对象,这些都涉及到内存的管理.以

cocos2d 内存管理机制

简单做下笔记,等有更深的理解时再补充. Cocos2d内存管理的基本原理是对象内存引用计数.当声明定义一个对象时,会在堆上为这个对象分配内存,并且有一个变量m_uReference专门用于记录该对象被引用了多少次. 内存引用计数的原理就是,当该对象被引用时m_uReference++,当该对象被取消引用时m_uReference--,若果m_uReference==0时,该对象就会被释放. 有了基本概念后,就不得不提及两个重要的函数--retain()和release().这两个函数是干什么用的