几乎所有的现代编程语言都使用动态内存分配,即允许进程在运行时分配或释放无法在编译期确定大小的对象,这些对象的存活时间有可能超出创建者的生存周期。动态分配的对象存在于堆(heap)中而不是栈(stack)或者静态区(statically)中。在内存的管理方式上,有两种方式:1)显示内存释放,由开发人员显示的创建或释放对象;2)自动动态内存管理,由编程语言的运行时系统(虚拟机)负责内存的回收。自动动态内存管理可以显著地降低开发成本,提供程序的健壮性。我们这篇文章主要便是讲述内存管理中常用垃圾回收算法,并结合 java 来分析内部的实现。

垃圾回收的目的是回收程序不再使用的对象所占用的空间,任何具备自动内存管理系统的语言都要有三个功能:

  1. 为新对象分配空间;
  2. 确定存活对象;
  3. 回收死亡对象所占用的空间。

内存分配与回收是相关性比较强的两个功能,内存管理系统都要具备这两个功能;在实现中,使用指针的可达性来近似对象的存活:只有当堆中存在一条从根出发的指针链能最终到达某个对象时,才能认定该对象存活,更进一步,如果不存在这一条指针链,则认为对象死亡,其空间可以得到回收。回收死亡对象包括两种方式:1)直接释放该对象,有可能会与前后的空闲对象进行合并;2)将存活对象进行移动或整理,减少内存碎片的问题。

阅读全文 »

1. 概述

处于优化的目的,在不同的编译器及不同体系架构的cpu 中,会将指令重排,即程序中排在后面的指令有可能比排在前面的指令先执行。同时由于cpu 中存在读写缓冲区,会将指令相关的运行时数据暂存在这些缓冲区中,也会导致指令重排的问题。目前的cpu 都是多核的体系架构,同一个语言编写的程序在不同硬件体系中,在多线程执行环境下,多次执行的结果可能不一致。在Java 中,怎么解决这个问题?为了屏蔽不同硬件架构的差异,给程序员提供一致的运行结果,Java 提出了 JMM(内存模型)的概念。

阅读全文 »

在数据库技术中,事务将应用程序的多个读、写操作捆绑在一起成为一个逻辑操作单元。即事务中的所有读写是一个执行的整体,整个事务要么成功(提交)、要么失败(中止或回滚)。如果失败,应用程序可以安全地重试。这样,由于不需要担心部分失败的情况(无论出于何种原因),应用层的错误处理就变得简单得多。

阅读全文 »

概括来讲,存储引擎分为两大类:针对事务处理(OLTP)优化的结构,以及针对分析型(OLAP)的优化结构。它们典型的访问模式存在很大的差异:

  • OLTP系统通常面向用户,这意味着它们可能收到大量的请求。为了处理负载,应用程序通常在每个用户查询中只涉及小量的记录。应用程序基于某种键来请求记录,而存储引擎使用索引来查找所请求的数据。磁盘寻道时间往往是瓶颈。
  • 由于不直接面对最终用户,数据仓库和类似的分析系统相对并不太广为人知,它们主要由业务分析师使用。处理的查询请求数目远低于OLTP系统,但每个查询通常要求非常苛刻,需要在短时间内扫描百万条记录。磁盘带宽(不是寻道时间)通常是瓶颈,而面向列的存储对于这种工作负载成为日益流行的解决方案。
    在OLTP方面,有两个主要流派的存储引擎:
  • 日志结构流派:它只允许追加方式更新文件和删除过时的文件,但不会修改已写入的文件。BitCask、SSTable、LST-Tree、LevelDB、Cassandra、HBase、Lucence等属于此类;
  • 原地更新流派:将磁盘视为可以覆盖的一组固定大小的页。B-Tree是这个哲学的最典型代表,它已用于所有主要的关系数据库,以及大量的非关系数据库。

日志结构的存储引擎是一个相对较新的方案。其关键思想是系统地将磁盘上随机访问写入转为顺序写入,由于硬盘驱动器和SSD的性能特性,可以实现更高的写入吞吐量。下面将针对这两种存储引擎进行分析。

阅读全文 »

在大多数软件系统中,功能特性决定了系统能做什么,而非功能特性决定了系统能走多远,本篇文章专注于非功能特性中三个比较重要的特性:

  • 可靠性(Reliability):当出现意外情况如硬件、软件故障、人为失误等,系统应可以继续正常运转:虽然性能可能有所降低,但确保功能正确。
  • 可扩展性(Scalability):随着规模的增长,例如数据量、流量或复杂性,系统应以合理的方式来匹配这种增长。
  • 可维护性(Maintainability):随着时间的推移,许多新的人员参与到系统开发和运维,以维护现有功能或适配新场景等,系统都应高效运转。
阅读全文 »

NameServer 作为消息中间件 RocketMQ 的核心组件之一, 起着注册中心的作用,这篇文章主要是从整体上分析一下NameServer的实现。
rocketmq-namesrv-overview
NameServer可以分为三个层次(暂且这么分,方便理解),1)通信层,使用 Netty 作为底层通信组件,封装统一的网络 IO 事件处理流程,同时用户也可以自定义事件。2)服务层,封装通用的业务逻辑:a)统一请求处理流程;b)定义三种调用方式,如同步调用,异步调用及单向调用(发出请求不需要响应数据);c)响应超时处理;d)IO事件处理。3)业务层,实现请求处理器及事件监听器注册接口,处理NameServer相关的数据,如broker地址及保活信息、topic队列信息及服务器过滤信息。

阅读全文 »

1. 概述

Java 8中的Stream是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(Aggregate operation),或者大批量数据操作(Bulk data operation)。Stream API借助于同样新出现的Lambda表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用fork/join并行方式来拆分任务和加速处理过程1

阅读全文 »

1. 概述

ForkJoinPool运用了Fork/Join原理,使用“分而治之”的思想,将大任务分拆成小任务,从而分配给多个线程并行执行,最后合并得到最终结果,加快计算。ForkJoinPool可以充分利用多cpu,多核cpu的优势,提高算法的执行效率,ForkJoinPool整体结构如下图所示:
fork-join

  • ForkJoinPool:框架的主体,存放了工作队列数组,对线程、工作队列及任务进行统一的管理。
  • WorkQueue:工作队列,是任务存储的容器,也是实现Work-Stealing的关键数据结构。
  • ForkJoinWorkerThread:工作线程,是任务的执行单元。
  • ForkJoinTask:封装业务的执行逻辑,包括任务fork及join流程。
阅读全文 »
0%