说到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(每二个节点)插入到合适它的位置。
相关推荐
《Apache服务器核心源码解析——聚焦于APR》 Apache HTTP Server(简称Apache)是全球最广泛使用的Web服务器,其背后的稳定性和安全性得益于其强大的核心组件之一:Apache Portable Runtime(APR)。APR是一个跨...
- **内存管理优化**:Apr提供了内存池功能,可以更有效地管理内存,降低垃圾回收压力。 然而,值得注意的是,虽然Apr和Tomcat Native能带来性能提升,但并非所有应用都能从中受益。对于小型应用或者非高并发场景,...
- **Buffer Pool**: 缓冲池技术用于减少内存分配和释放的开销。 7. **故障排查与调试** - **日志系统**: Tomcat的日志系统可以帮助开发者跟踪和定位问题,源码中包含了丰富的日志输出选项。 - **JMX(Java ...
3. **源码解析** - **catalina**:Catalina模块的源码,包括容器、连接器、管道等组件。 - **coyote**:Coyote模块的源码,负责处理HTTP请求。 - **jasper**:JSP引擎的源码,涉及JSP的编译和执行。 - **jdbc-...
Jasper在解析JSP文件时生成对应的Java源代码,然后通过Catalina的Class Loader加载到内存中运行。 - **Coyote**:处理HTTP请求和响应,是Tomcat与网络通信的接口。它提供了连接器(Connector)组件,负责接收和...
简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级、中级、高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情、执着,对IT的憧憬、向往!...
每个连接器都有一个固定的线程池,当请求到达时,会从线程池中获取一个空闲线程来处理,处理完成后线程返回到池中。这种方式可以有效避免线程创建和销毁的开销。 7. **安全性**:Tomcat提供了一系列安全特性,如SSL...
优化Tomcat涉及多个方面,如调整JVM参数、增大堆内存、启用连接器优化、使用NIO或APR connector、减少session数量等。对于高并发场景,可能还需要考虑负载均衡和集群配置。 **Tomcat部署** 部署Web应用到Tomcat...
在源码中,我们可以学习到线程池的实现、内存管理策略、网络通信技术,甚至可以了解Tomcat如何实现与其他应用服务器的集成,如JNDI服务、JMX监控等。 总之,Tomcat 6.0的jar包和源代码为我们提供了一个全面了解和...
它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...
它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...
它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...
它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...
它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...
它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...
它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...
它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...
它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...
它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...
它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...