`
openxtiger
  • 浏览: 150832 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

[APR源码解析]内存池

阅读更多

说到apr的内存池,必须知道如何构建一个环形双向链表。因为apr内存池apr_pool_t的active就是一个环形双向链表。

 

#define list_insert(node, point) do {           \  
    node->ref = point->ref;                     \  
    *node->ref = node;                          \
    node->next = point;                         \
    point->ref = &node->next;                   \
} while (0)

这个micro的用处,是将point插到node的前面。 假设point前的节点为bpoint。

 

node->ref = point->ref=&bpoint->next。因此*node->ref=bpoint->next=node,这样就让bpoint的next指向了node.

经过node->next=point 后。就可以得到链表:bpoint->next=node,node->next=point。point->ref=&node->next,node->ref=&bpoint->next。所以很巧妙的是ref,他既能表示指向自己(用**ref获取),用能让上一个节点的next(用*ref获取)指向其他节点。插入时,用他来指向新节点。删除时,用他来指向下一个节点。下面的代码就实现这个。

 

 

#define list_remove(node) do {                  \
    *node->ref = node->next;                    \
    node->next->ref = node->ref;                \
} while (0)

 还有一种特殊情况:目前系统只有一个节点。即bpoint=point。此时就得模拟一个bpoint(bpoint=point)。

bpoint->next = point

point->ref = &bpoint->next

这个代码在apr_pool_create_ex就会出现。

node->next = node;
node->ref = &node->next;

 有了以上基础,内存池处理就简单了。

 

 1.apr_pool_initialize内存池初始化

主要作用是通过apr_pool_create_ex创建一个apr_pool_t,此pool放在一个node节点中。此node又放在了pool的active中,形成一个active环形双向链表的第一个节点。当然也同时实现了对bpoint的模拟。

 2.apr_palloc内存分配

 

APR_DECLARE(void *) apr_palloc(apr_pool_t *pool, apr_size_t in_size)
{
    apr_memnode_t *active, *node;
    void *mem;
    apr_size_t size, free_index;

    size = APR_ALIGN_DEFAULT(in_size);
    if (size < in_size) {
        if (pool->abort_fn)
            pool->abort_fn(APR_ENOMEM);

        return NULL;
    }
    active = pool->active;

    /* If the active node has enough bytes left, use it. */
    if (size <= node_free_space(active)) {
        mem = active->first_avail;
        active->first_avail += size;

        return mem;
    }

    node = active->next;
    if (size <= node_free_space(node)) {
        list_remove(node);
    }
    else {
        if ((node = allocator_alloc(pool->allocator, size)) == NULL) {
            if (pool->abort_fn)
                pool->abort_fn(APR_ENOMEM);

            return NULL;
        }
    }

    node->free_index = 0;

    mem = node->first_avail;
    node->first_avail += size;

    list_insert(node, active);

    pool->active = node;

    free_index = (APR_ALIGN(active->endp - active->first_avail + 1,
                            BOUNDARY_SIZE) - BOUNDARY_SIZE) >> BOUNDARY_INDEX;

    active->free_index = (APR_UINT32_TRUNC_CAST)free_index;
    node = active->next;
    if (free_index >= node->free_index)
        return mem;

    do {
        node = node->next;
    }
    while (free_index < node->free_index);

    list_remove(active);
    list_insert(active, node);

    return mem;
}
分配的原则是,如果pool的active的第一节点(node1,为了方便,以下第二节点称为node2,第三为node3......)有足够内存则直接返回内存。mem指向内存的首地址。并将node的内存剩余空间扣去size。如果pool的node1不足,则选择node2,如果node2足够则使用node2(此时要将这个节点先拿出来list_remove(node);),否则就用分配子分配一个node。并将node插入到第一个节点中( list_insert(node, active);)。 此时的active就成为第二节点。

 

active = pool->active;

    /* If the active node has enough bytes left, use it. */
    if (size <= node_free_space(active)) {
        mem = active->first_avail;
        active->first_avail += size;

        return mem;
    }

    node = active->next;
    if (size <= node_free_space(node)) {
        list_remove(node);
    }
    else {
        if ((node = allocator_alloc(pool->allocator, size)) == NULL) {
            if (pool->abort_fn)
                pool->abort_fn(APR_ENOMEM);

            return NULL;
        }
    }

    node->free_index = 0;

    mem = node->first_avail;
    node->first_avail += size;

    list_insert(node, active);

    pool->active = node;

 

 为了下次分配效率更高,此时需将active这个环形双向链表做一些调整。确保第二个节点为所有节点剩余空间最多的(即从第二节点开始按剩余空间的大小降序排列)。这样下一次分配空间时,就只要判断以上代码。

 

降序排列是怎么实现呢?其实只要将每次的node2节点放到适合他的位置就可以,上一次是降序,因为只有node2会改变大小。所以只要将node2放到合适位置去,从第二个节点开始后的链表还是降序。

那如何放node2呢?

第一,获取node2的free_index。

 

 

free_index = (APR_ALIGN(active->endp - active->first_avail + 1,
                            BOUNDARY_SIZE) - BOUNDARY_SIZE) >> BOUNDARY_INDEX;

    active->free_index = (APR_UINT32_TRUNC_CAST)free_index;
 第二,和一下节点(node3)的free_index比较,如果大于等于node3则,什么都不用变化。如果小于,则找一个合适的位置。

 

 

do {
        node = node->next;
    }
    while (free_index < node->free_index);
 如果node2这个节点的内存大小比任何一个节点的大小都要小,这样怎么办。应该把位置放哪里?这就是apr巧妙的地方,因为active是一个环形链表,所以最后个节点的下一个节点就是node1。代码中有一句 node->free_index = 0;(此时的node就是node1),这一句相信很多人都不知为什么要这样做。其实就是要让以上的循环永远可以终止。因为node2的剩余大小肯定大于0,所以node2这个节点的内存大小比任何一个节点的大小都要小时,其实他会大于第一个节点。这样他就会被插在第一个节点的前面,也就是成为最后一个节点

 

list_remove(active);
    list_insert(active, node);

 这个代码实现将active(每二个节点)插入到合适它的位置。

 

 

 

1
0
分享到:
评论

相关推荐

    apr.rar_APR 服务器

    《Apache服务器核心源码解析——聚焦于APR》 Apache HTTP Server(简称Apache)是全球最广泛使用的Web服务器,其背后的稳定性和安全性得益于其强大的核心组件之一:Apache Portable Runtime(APR)。APR是一个跨...

    java源码:Tomcat 安装apr 支持 Tomcat Native.zip

    - **内存管理优化**:Apr提供了内存池功能,可以更有效地管理内存,降低垃圾回收压力。 然而,值得注意的是,虽然Apr和Tomcat Native能带来性能提升,但并非所有应用都能从中受益。对于小型应用或者非高并发场景,...

    tomcat源码研读笔记中的tomcat源码

    - **Buffer Pool**: 缓冲池技术用于减少内存分配和释放的开销。 7. **故障排查与调试** - **日志系统**: Tomcat的日志系统可以帮助开发者跟踪和定位问题,源码中包含了丰富的日志输出选项。 - **JMX(Java ...

    apache-tomcat-8.5.72-src.zip

    3. **源码解析** - **catalina**:Catalina模块的源码,包括容器、连接器、管道等组件。 - **coyote**:Coyote模块的源码,负责处理HTTP请求。 - **jasper**:JSP引擎的源码,涉及JSP的编译和执行。 - **jdbc-...

    how-tomcat-works.docx

    Jasper在解析JSP文件时生成对应的Java源代码,然后通过Catalina的Class Loader加载到内存中运行。 - **Coyote**:处理HTTP请求和响应,是Tomcat与网络通信的接口。它提供了连接器(Connector)组件,负责接收和...

    JAVA上百实例源码以及开源项目源代码

    简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级、中级、高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情、执着,对IT的憧憬、向往!...

    How Tomcat Works

    每个连接器都有一个固定的线程池,当请求到达时,会从线程池中获取一个空闲线程来处理,处理完成后线程返回到池中。这种方式可以有效避免线程创建和销毁的开销。 7. **安全性**:Tomcat提供了一系列安全特性,如SSL...

    apache-tomcat-7.0.4-src.zip 天涯浪子

    优化Tomcat涉及多个方面,如调整JVM参数、增大堆内存、启用连接器优化、使用NIO或APR connector、减少session数量等。对于高并发场景,可能还需要考虑负载均衡和集群配置。 **Tomcat部署** 部署Web应用到Tomcat...

    tomcat6.0 jar包及源代码

    在源码中,我们可以学习到线程池的实现、内存管理策略、网络通信技术,甚至可以了解Tomcat如何实现与其他应用服务器的集成,如JNDI服务、JMX监控等。 总之,Tomcat 6.0的jar包和源代码为我们提供了一个全面了解和...

    java开源包8

    它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...

    java开源包1

    它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...

    java开源包11

    它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...

    java开源包2

    它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...

    java开源包3

    它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...

    java开源包6

    它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...

    java开源包5

    它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...

    java开源包10

    它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...

    java开源包4

    它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...

    java开源包7

    它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...

    java开源包9

    它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...

Global site tag (gtag.js) - Google Analytics