iOS底层探索之自动释放池原理解析

目录
  • 1、概述
  • 2、底层探索
    • 2.1、打印自动释放池结构
    • 2.2、objc_autoreleasePoolPush
      • 2.2.1、AutoreleasePoolPage
      • 2.2.2、AutoreleasePoolPageData
      • 2.2.3、push(对象压栈)
      • 2.2.4、池页容量
    • 2.3、objc_autoreleasePoolPop
      • 2.3.1、pop(对象出栈)
      • 2.3.2、popPage
      • 2.3.3、releaseUntil
      • 2.3.4、kill
  • 3、嵌套使用
  • 总结

1、概述

  • OC 中的一种内存自动回收机制,它可以将加入AutoreleasePool中的变量release的时机延迟
  • 当创建一个对象,在正常情况下,变量会在超出其作用域时立即 release ,如果将其加入到自动释放池中,这个对象并不会立即释放,而会等到 runloop 休眠 / 超出autoreleasepool作用域之后进行释放

  • 从程序启动到加载完成,主线程对应的 Runloop 会处于休眠状态,等待用户交互来唤醒 Runloop
  • 用户每次交互都会启动一次 Runloop ,用于处理用户的所有点击、触摸等事件
  • Runloop 在监听到交互事件后,就会创建自动释放池,并将所有延迟释放的对象添加到自动释放池中
  • 在一次完整的 Runloop 结束之前,会向自动释放池中所有对象发送 release 消息,然后销毁自动释放池

2、底层探索

准备简单代码

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {

    @autoreleasepool {
        NSLog(@"Hello, World!");
    }
    return 0;
}

转换成.cpp文件:

clang -rewrite-objc main.m -o main.cpp

int main(int argc, const char * argv[]) {

    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_jl_d06jlfkj2ws74_5g45kms07m0000gn_T_main_da0d58_mi_0);
    }
    return 0;
}
  • autoreleasepool 变成了__AtAutoreleasePool类型声明的代码
struct __AtAutoreleasePool {
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};
  • 前边了解过这种写法是在结构体 构造 时调用objc_autoreleasePoolPush函数,在 结构体退出作用域析构 时调用objc_autoreleasePoolPop函数,这两个函数也是下边研究的重点(在 main 函数中 autoreleasepool 处设置断点查看汇编也可以看到这两个函数的符号调用)

2.1、打印自动释放池结构

  • 测试项目,关闭ARC模式

  • 手动添加一个对象到自动释放池,并打印自动释放池结构
// 导入 _objc_autoreleasePoolPrint 函数,用于打印自动释放池的结构
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *objc = [[[NSObject alloc] init] autorelease];
        _objc_autoreleasePoolPrint();
    }
    return 0;
}
##############
AUTORELEASE POOLS for thread 0x1000ebe00
2 releases pending.
[0x10700b000]  ................  PAGE  (hot) (cold)
[0x10700b038]  ################  POOL 0x10700b038
[0x10700b040]       0x100705f60  NSObject
##############
  • _objc_autoreleasePoolPrint调用 AutoreleasePoolPage::printAll()通过AutoreleasePoolPage 的命名空间调用printAll());按照自动释放池的结构,通过双向链表遍历page,依次读取 page 中的内容并进行打印
  • 打印了当前自动释放池所属线程,与 2 个需要释放的对象:哨兵对象:POOL手动加入自动释放池 的对象objc
  • 当前的Page信息,占56字节,因为只有一页,即是冷页面,也是热页面

2.2、objc_autoreleasePoolPush

void *
objc_autoreleasePoolPush(void)
{
    // 调用 AutoreleasePoolPage 命名空间下的 push 函数
    return AutoreleasePoolPage::push();
}

2.2.1、AutoreleasePoolPage

AutoreleasePoolPage的定义,能看到这样一段注释

/***********************************************************************
   Autorelease pool implementation

   A thread's autorelease pool is a stack of pointers.
   线程的自动释放池是一个指针堆栈

   Each pointer is either an object to release, or POOL_BOUNDARY which is an autorelease pool boundary.
   每个指针要么是一个要释放的对象,要么是POOL_BOUNDARY自动释放池边界

   A pool token is a pointer to the POOL_BOUNDARY for that pool. When the pool is popped, every object hotter than the sentinel is released.
   池令牌是指向该池的POOL_BOUNDARY的指针。当池被弹出,每个比哨兵热的对象都被释放

   The stack is divided into a doubly-linked list of pages. Pages are added and deleted as necessary.
   堆栈被分成一个双链接的页面列表。根据需要添加和删除页面

   Thread-local storage points to the hot page, where newly autoreleased objects are stored.
   线程本地存储指向热页,其中存储新自动释放的对象
**********************************************************************/

AutoreleasePoolPage 继承于AutoreleasePoolPageData(有用的内容基本都在 AutoreleasePoolPageData 结构体中)

class AutoreleasePoolPage : private AutoreleasePoolPageData
{
	friend struct thread_data_t;

public:
	static size_t const SIZE =
#if PROTECT_AUTORELEASEPOOL
		PAGE_MAX_SIZE;  // must be multiple of vm page size
#else
		PAGE_MIN_SIZE;  // size and alignment, power of 2
#endif

private:
	static pthread_key_t const key = AUTORELEASE_POOL_KEY;
	static uint8_t const SCRIBBLE = 0xA3;  // 0xA3A3A3A3 after releasing
	static size_t const COUNT = SIZE / sizeof(id);
    static size_t const MAX_FAULTS = 2;

    ...
}

2.2.2、AutoreleasePoolPageData

class AutoreleasePoolPage;
struct AutoreleasePoolPageData
{
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
    struct AutoreleasePoolEntry {
        uintptr_t ptr: 48;
        uintptr_t count: 16;

        static const uintptr_t maxCount = 65535; // 2^16 - 1
    };
    static_assert((AutoreleasePoolEntry){ .ptr = MACH_VM_MAX_ADDRESS }.ptr == MACH_VM_MAX_ADDRESS, "MACH_VM_MAX_ADDRESS doesn't fit into AutoreleasePoolEntry::ptr!");
#endif

	magic_t const magic;
	__unsafe_unretained id *next;
	pthread_t const thread;
	AutoreleasePoolPage * const parent;
	AutoreleasePoolPage *child;
	uint32_t const depth;
	uint32_t hiwat;

	AutoreleasePoolPageData(__unsafe_unretained id* _next, pthread_t _thread, AutoreleasePoolPage* _parent, uint32_t _depth, uint32_t _hiwat)
		: magic(), next(_next), thread(_thread),
		  parent(_parent), child(nil),
		  depth(_depth), hiwat(_hiwat)
	{
	}
};

结构体中,包含以下成员变量:(根据下边的成员大小得出 一个page占56字节

  • magic:用来校验 AutoreleasePoolPage 的结构是否完整(16字节
  • next:指向最新添加的autoreleased对象的下一个位置,初始化时执行begin():获取对象压栈的起始位置(8字节
  • thread:指向当前线程(8字节
  • parent:指向父节点,第一个节点的parent值为nil8字节
  • child:指向子节点,最后一个节点的child值为nil8字节
  • depth:代表深度,从0开始,往后递增14字节
  • hiwat:代表high water mark最大入栈数量标记(4字节

2.2.3、push(对象压栈)

static inline void *push()
{
    id *dest;
    if (slowpath(DebugPoolAllocation)) {
        // Each autorelease pool starts on a new pool page.
        dest = autoreleaseNewPage(POOL_BOUNDARY);
    } else {
        //
        dest = autoreleaseFast(POOL_BOUNDARY);
    }
    ASSERT(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
    return dest;
}
  • DebugPoolAllocation:当自动释放池按顺序弹出时停止,并允许堆调试器跟踪自动释放池
  • 不存在,调用autoreleaseNewPage函数,从一个新的池页开始创建
  • 否则,调用autoreleaseFast函数,将哨兵对象压栈

autoreleaseFast

  • 若存在 page,且未存满,调用add函数
  • 若存在 page,但已存满,调用autoreleaseFullPage函数

    遍历链表,找到最后一个空白的子页面

    对其进行创建新页

    设置为热页面

    添加对象

  • 不存在 page,调用autoreleaseNoPage函数调用 AutoreleasePoolPage 构造函数,创建新页
    • 通过父类 AutoreleasePoolPageData 进行初始化
    • begin:获取对象压栈的起始位置(sizeof(*this):大小取决于自身结构体中的成员变量、返回对象可压栈的真正开始地址,在成员变量以下)
    • objc_thread_self:通过tls获取当前线程
    • 链接双向链表

    设置为热页面

    pushExtraBoundaryYES,哨兵对象压栈

    对象压栈

2.2.4、池页容量

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        for (int i = 0; i < 505; i++) {
            NSObject *objc = [[[NSObject alloc] init] autorelease];
        }

        _objc_autoreleasePoolPrint();
    }
    return 0;
}

-------------------------
objc[1804]: ##############
objc[1804]: AUTORELEASE POOLS for thread 0x1000ebe00
objc[1804]: 506 releases pending.
objc[1804]: [0x10200c000]  ................  PAGE (full)  (cold)
objc[1804]: [0x10200c038]  ################  POOL 0x10200c038
objc[1804]: [0x10200c040]       0x100638420  NSObject
objc[1804]: [0x10200c048]       0x100637a40  NSObject
objc[1804]: [0x10200c050]       0x100636970  NSObject
...
objc[1804]: [0x100809000]  ................  PAGE  (hot)
objc[1804]: [0x100809038]       0x10063a0b0  NSObject
objc[1804]: ##############
  • 505NSObject 对象循环加入自动释放池,当存储 504 个对象时,池页已满,第 505 个对象创建新池页存储
  • 一页的容量:504 * 8 = 4032,加上56字节成员变量和8字节哨兵对象,共计4096字节
  • 每一页都存在56字节的成员变量
  • 一个自动释放池,只会压栈一个哨兵对象

2.3、objc_autoreleasePoolPop

void
objc_autoreleasePoolPop(void *ctxt)
{
    AutoreleasePoolPage::pop(ctxt);
}

2.3.1、pop(对象出栈)

static inline void
pop(void *token)
{
    AutoreleasePoolPage *page;
    id *stop;
    //判断当前对象是否为空占位符
    if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
        // Popping the top-level placeholder pool.
        //获取热页面
        page = hotPage();
        if (!page) {
            // Pool was never used. Clear the placeholder.
            //不存在热页面,将标记设置为nil
            return setHotPage(nil);
        }
        // Pool was used. Pop its contents normally.
        // Pool pages remain allocated for re-use as usual.
        //存在热页面,通过双向链表循环向上找到最冷页面
        page = coldPage();
        //将token设置为起始位置
        token = page->begin();
    } else {
        //获取token所在的页
        page = pageForPointer(token);
    }

    //赋值给stop
    stop = (id *)token;
    //当前位置不是哨兵对象
    if (*stop != POOL_BOUNDARY) {
        if (stop == page->begin()  &&  !page->parent) {
            // Start of coldest page may correctly not be POOL_BOUNDARY:
            // 1. top-level pool is popped, leaving the cold page in place
            // 2. an object is autoreleased with no pool

            //最冷页面的起始可能不是POOL_BOUNDARY:
            //1. 弹出顶级池,保留冷页面
            //2. 对象在没有池的情况下被自动释放
        } else {
            // Error. For bincompat purposes this is not
            // fatal in executables built with old SDKs.
            //出现异常情况
            return badPop(token);
        }
    }

    if (slowpath(PrintPoolHiwat || DebugPoolAllocation || DebugMissingPools)) {
        return popPageDebug(token, page, stop);
    }

    //出栈
    return popPage<false>(token, page, stop);
}

2.3.2、popPage

static void
popPage(void *token, AutoreleasePoolPage *page, id *stop)
{
    if (allowDebug && PrintPoolHiwat) printHiwat();

    //当前页中对象出栈,到stop位置停止
    page->releaseUntil(stop);

    // memory: delete empty children
    if (allowDebug && DebugPoolAllocation  &&  page->empty()) {
        // special case: delete everything during page-per-pool debugging
        //特殊情况:在逐页池调试期间删除所有内容

        //获取父页面
        AutoreleasePoolPage *parent = page->parent;
        //销毁当前页面
        page->kill();
        //将父页面设置为热页面
        setHotPage(parent);
    } else if (allowDebug && DebugMissingPools  &&  page->empty()  &&  !page->parent) {
        // special case: delete everything for pop(top)
        // when debugging missing autorelease pools
        //特殊情况:删除所有的pop

        //销毁当前页面
        page->kill();
        //将热页面标记设置为nil
        setHotPage(nil);
    } else if (page->child) {
        // hysteresis: keep one empty child if page is more than half full
        //如果页面超过一半,则保留一个空子页面
        if (page->lessThanHalfFull()) {
            page->child->kill();
        }
        else if (page->child->child) {
            page->child->child->kill();
        }
    }
}

2.3.3、releaseUntil

void releaseUntil(id *stop)
{
    // Not recursive: we don't want to blow out the stack
    // if a thread accumulates a stupendous amount of garbage

    //向下遍历,到stop停止
    while (this->next != stop) {
        // Restart from hotPage() every time, in case -release
        // autoreleased more objects

        //获取热页面
        AutoreleasePoolPage *page = hotPage();

        // fixme I think this `while` can be `if`, but I can't prove it
        //如果当前页面中没有对象
        while (page->empty()) {
            //获取父页面
            page = page->parent;
            //标记为热页面
            setHotPage(page);
        }

        page->unprotect();
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
        AutoreleasePoolEntry* entry = (AutoreleasePoolEntry*) --page->next;

        // create an obj with the zeroed out top byte and release that
        id obj = (id)entry->ptr;
        int count = (int)entry->count;  // grab these before memset
#else
        //内存平移,获取对象
        id obj = *--page->next;
#endif
        memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
        page->protect();

        //当前对象不是哨兵对象
        if (obj != POOL_BOUNDARY) {
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
            // release count+1 times since it is count of the additional
            // autoreleases beyond the first one
            for (int i = 0; i < count + 1; i++) {
                objc_release(obj);
            }
#else
            //将其释放
            objc_release(obj);
#endif
        }
    }

    //将当前页面标记为热页面
    setHotPage(this);

#if DEBUG
    // we expect any children to be completely empty
    for (AutoreleasePoolPage *page = child; page; page = page->child) {
        ASSERT(page->empty());
    }
#endif
}

2.3.4、kill

 void kill()
{
    // Not recursive: we don't want to blow out the stack
    // if a thread accumulates a stupendous amount of garbage
    AutoreleasePoolPage *page = this;
    //循环找到最后一个子页面
    while (page->child) page = page->child;

    AutoreleasePoolPage *deathptr;
    do {
        deathptr = page;
        //找到父页面
        page = page->parent;
        if (page) {
            //将子页面设置为nil
            page->unprotect();
            page->child = nil;
            page->protect();
        }
        //销毁子页面
        delete deathptr;

        //遍历销毁到this为止
    } while (deathptr != this);
}

3、嵌套使用

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        NSObject *objc = [[[NSObject alloc] init] autorelease];

        @autoreleasepool {
            NSObject *objc = [[[NSObject alloc] init] autorelease];
        }

        _objc_autoreleasePoolPrint();
    }
    return 0;
}

-------------------------
objc[2511]: ##############
objc[2511]: AUTORELEASE POOLS for thread 0x1000ebe00
objc[2511]: 4 releases pending.
objc[2511]: [0x10680d000]  ................  PAGE  (hot) (cold)
objc[2511]: [0x10680d038]  ################  POOL 0x10680d038
objc[2511]: [0x10680d040]       0x101370c40  NSObject
objc[2511]: [0x10680d048]  ################  POOL 0x10680d048
objc[2511]: [0x10680d050]       0x101365fb0  NSObject
objc[2511]: ##############
  • 线程的自动释放池是一个指针堆栈,当嵌套使用时,添加好各自堆栈的哨兵对象;出栈时,先释放内部,再释放外部

总结

结构:

  • 自动释放池的压栈和出栈,通过结构体的构造函数和析构函数触发
  • 压栈:调用objc_autoreleasePoolPush函数
  • 出栈:调用objc_autoreleasePoolPop函数

特点:

  • 自动释放池是一个存储指针的栈结构
  • 指针要么是一个要释放的对象,要么是POOL_BOUNDARY自动释放池边界,俗称:哨兵对象
  • 哨兵对象的作用:当自动释放池将对象进行pop操作时,需要知道边界在哪里,否则会破坏别人的内存空间。而哨兵对象,就是边界标识
  • 自动释放池的栈空间被分成一个 双链接 结构的页面列表,可添加和删除页面
  • 双向链表的特别,一个页中同时存在父节点和子节点。可向前找到父页面,也可向后找到子页面
  • 线程本地存储指向热页,其中存储新自动释放的对象
  • 栈原则,先进后出,可以理解为最后一个页面就是热页。里面的对象最后被push,最先被pop

容量:

  • 池页大小为4096字节,每一页都包含56字节的成员变量,但一个自动释放池中,只会压栈一个哨兵对象,占8字节

原理:

  • 自动释放池的本质是__AtAutoreleasePool结构体,包含构造函数和析构函数
  • 结构体声明,触发构造函数,调用objc_autoreleasePoolPush函数,本质是对象压栈的push方法
  • 当结构体出作用域空间,触发析构函数,调用objc_autoreleasePoolPop函数,本质是对象出栈的pop方法
  • 对象压栈
    • 如果存在page,并且没有存满,调用add函数

      • 使用*next++进行内存平移
      • 将对象压栈
      • 如果存在page,但存储已满,调用autoreleaseFullPage函数
      • 遍历链表,找到最后一个空白的子页面
      • 对其进行创建新页
      • 设置为热页面
      • 添加对象
    • 否则,不存在page,调用autoreleaseNoPage函数
      • 通过父类AutoreleasePoolPageData进行初始化
      • begin:获取对象压栈的起始位置
      • objc_thread_self:通过tls获取当前线程
      • 链接双向链表
      • 设置为热页面
      • pushExtraBoundaryYES,哨兵对象压栈
      • 对象压栈
  • 对象出栈
    • 调用popPage函数,传入stop为哨兵对象的位置
    • 当前页中对象出栈,到stop位置停止
    • 调用kill函数,销毁当前页面

嵌套使用:

  • 线程的自动释放池是一个指针堆栈,当嵌套使用时,添加好各自堆栈的哨兵对象。出栈时,先释放内部,再释放外部

ARC模式:

  • ARC模式,使用allocnewcopymutableCopy前缀开头的方法进行对象创建,不会加入到自动释放池;它们的空间开辟由开发者申请,释放也由开发者进行管理

与线程的关系:

  • 每个线程(包括主线程)维护自己的对象堆栈。随着新池的创建,它们被添加到堆栈的顶部。当池被释放时,它们会从堆栈中移除
  • autoreleased对象被放置在当前线程的顶部自动释放池中;当一个线程终止时,它会自动清空所有与其关联的自动释放池

Runloop 的关系:

  • 主程序在事件循环的每个循环开始时在主线程上创建一个自动释放池
  • 并在结束时将其排空,从而释放在处理事件时生成的任何自动释放对象

使用:

  • for循环中大量创建对象时,使用 autorelease 可以有效控制内存的快速增长(原因是释放没有创建快,如果不加 autorelease 最终内存也会降下来,但可以 减少内存峰值
for (int i = 0; i<100000000; i++) {
    @autoreleasepool {
        NSLog(@"%d",i);
        __autoreleasing LZPerson *p =[LZPerson new];
    }
}

到此这篇关于iOS底层探索之自动释放池的文章就介绍到这了,更多相关iOS自动释放池内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • iOS调试Block引用对象无法被释放的小技巧分享

    Block技术在iOS开发中非常流行也很方便,但是稍微疏忽就可能会产生引用无法被释放的问题,从而造成内存泄漏.那如何知道哪个Block持有了对象并造成内存泄漏呢? 一个解决的方法是在程序运行时通过Xcode的Debug Memory Graph 来查看当前进程中所有生命周期内的对象.这样可以在调试时通过这个功能发现一些本来应该被释放但是却没有被释放的对象.从而确定哪些对象有内存泄漏的嫌疑. 当点击某个对象时,右边可以看出这个对象的内存分配情况以及被引用的情况,从而可以进一步跟踪确认出对象是被谁持

  • iOS底层探索之自动释放池原理解析

    目录 1.概述 2.底层探索 2.1.打印自动释放池结构 2.2.objc_autoreleasePoolPush 2.2.1.AutoreleasePoolPage 2.2.2.AutoreleasePoolPageData 2.2.3.push(对象压栈) 2.2.4.池页容量 2.3.objc_autoreleasePoolPop 2.3.1.pop(对象出栈) 2.3.2.popPage 2.3.3.releaseUntil 2.3.4.kill 3.嵌套使用 总结 1.概述 OC 中的

  • SpringBoot2.0 中 HikariCP 数据库连接池原理解析

    作为后台服务开发,在日常工作中我们天天都在跟数据库打交道,一直在进行各种CRUD操作,都会使用到数据库连接池.按照发展历程,业界知名的数据库连接池有以下几种:c3p0.DBCP.Tomcat JDBC Connection Pool.Druid 等,不过最近最火的是 HiKariCP. HiKariCP 号称是业界跑得最快的数据库连接池,自从 SpringBoot 2.0 将其作为默认数据库连接池后,其发展势头锐不可当.那它为什么那么快呢?今天咱们就重点聊聊其中的原因. 一.什么是数据库连接池

  • Activiti工作流学习笔记之自动生成28张数据库表的底层原理解析

    网上关于工作流引擎Activiti生成表的机制大多仅限于四种策略模式,但其底层是如何实现的,相关文章还是比较少,因此,觉得撸一撸其生成表机制的底层原理. 我接触工作流引擎Activiti已有两年之久,但一直都只限于熟悉其各类API的使用,对底层的实现,则存在较大的盲区. Activiti这个开源框架在设计上,其实存在不少值得学习和思考的地方,例如,框架用到以命令模式.责任链模式.模板模式等优秀的设计模式来进行框架的设计. 故而,是值得好好研究下Activiti这个框架的底层实现. 我在工作当中现

  • iOS开发探索多线程GCD常用函数

    目录 正文 单例 栅栏函数 调度组 dispatch_group_t 信号量 dispatch_semaphore_t dispatch_source 总结 正文 前篇文章我们了解了GCD的任务的原理,接下来我们在探索一下GCD中我们开发常用的函数 单例 下面我们从源码中看一下我们创建单例的时候使用的dispatch_once,都做了什么,是通过什么操作保证全局唯一的 void dispatch_once(dispatch_once_t *val, dispatch_block_t block)

  • iOS开发探索多线程GCD队列示例详解

    目录 引言 进程与线程 1.进程的定义 2.线程的定义 3. 进程和线程的关系 4. 多线程 5. 时间片 6. 线程池 GCD 1.任务 2.队列 3.死锁 总结 引言 在iOS开发过程中,绕不开网络请求.下载图片之类的耗时操作,这些操作放在主线程中处理会造成卡顿现象,所以我们都是放在子线程进行处理,处理完成后再返回到主线程进行展示. 多线程贯穿了我们整个的开发过程,iOS的多线程操作有NSThread.GCD.NSOperation,其中我们最常用的就是GCD. 进程与线程 在了解GCD之前

  • MySQL的线程池原理学习教程

    线程池是Mysql5.6的一个核心功能,对于服务器应用而言,无论是web应用服务还是DB服务,高并发请求始终是一个绕不开的话题.当有大量请求并发访问时,一定伴随着资源的不断创建和释放,导致资源利用率低,降低了服务质量.线程池是一种通用的技术,通过预先创建一定数量的线程,当有请求达到时,线程池分配一个线程提供服务,请求结束后,该线程又去服务其他请求. 通过这种方式,避免了线程和内存对象的频繁创建和释放,降低了服务端的并发度,减少了上下文切换和资源的竞争,提高资源利用效率.所有服务的线程池本质都是位

  • 基于Apache组件分析对象池原理的实现案例分析

    目录 一.设计与原理 1.基础案例 2.接口设计 1.1 PooledObjectFactory 接口 1.2 ObjectPool 接口 1.3 PooledObject 接口 3.运行原理 二.构造分析 1.对象池 2.双端队列 三.对象管理 1.添加对象 2.借用对象 3.归还对象 4.对象状态 四.Redis应用 1.配置管理 2.源码分析 2.1 配置转换 2.2 对象池构造 2.3 对象管理 五.参考源码 池塘里养:Object: 一.设计与原理 1.基础案例 首先看一个基于comm

  • ThreadPoolExecutor线程池原理及其execute方法(详解)

    jdk1.7.0_79 对于线程池大部分人可能会用,也知道为什么用.无非就是任务需要异步执行,再者就是线程需要统一管理起来.对于从线程池中获取线程,大部分人可能只知道,我现在需要一个线程来执行一个任务,那我就把任务丢到线程池里,线程池里有空闲的线程就执行,没有空闲的线程就等待.实际上对于线程池的执行原理远远不止这么简单. 在Java并发包中提供了线程池类--ThreadPoolExecutor,实际上更多的我们可能用到的是Executors工厂类为我们提供的线程池:newFixedThreadP

  • Java 线程池原理深入分析

    Java 线程池原理 Executor框架的两级调度模型 在HotSpot VM的模型中,Java线程被一对一映射为本地操作系统线程.JAVA线程启动时会创建一个本地操作系统线程,当JAVA线程终止时,对应的操作系统线程也被销毁回收,而操作系统会调度所有线程并将它们分配给可用的CPU. 在上层,JAVA程序会将应用分解为多个任务,然后使用应用级的调度器(Executor)将这些任务映射成固定数量的线程:在底层,操作系统内核将这些线程映射到硬件处理器上. Executor框架类图 在前面介绍的JA

  • JAVA线程池原理实例详解

    本文实例讲述了JAVA线程池原理.分享给大家供大家参考,具体如下: 线程池的优点 1.线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以重复使用. 2.可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃. 线程池的创建 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQu

随机推荐