终于舍得出来写下博客了。我们知道Log4j目前已经停止更新了。Apache推出了新的Log4j2来代替Log4j,Log4j2是对Log4j的升级,与其前身Log4j相比有了显着的改进,并提供了许多Logback可用的改进,同时解决了Logback体系结构中的一些固有问题.
Log4j2的效率可以在多线程时,在线程数量大的情况下,超过logback10倍左右!(在asyncRoot中可以添加includeLocation="true"属性,该属性如果为true,可以携带类名和行号等信息,但是抽取这些信息,会有一些性能损耗)
其实log4j2 性能为啥能这么好主要还是采用了disruptor队列
log4j2内部内置了一个BlockingQueue队列,具体实现采用了ArrayBlockingQueue
可以看到ArrayBlockingQueue采用的是加锁的方式来处理线程安全问题的。
加锁的问题,虽然历代JDK一直投入大量的精力去解决问题,比如优化Sync关键字的实现方式、添加读写锁等,但是由于结构特性的问题,一直无法从根本上解决性能开销问题。
题外话 _ 除了锁的问题,还牵涉到底层CPU在计算时读取内存数据的问题。
举个例子:当你遍历一个数组时(数组在CPU计算时,是简单数据结构),你读取到第一个元素时,其他元素也会相应的放入1级缓存(距离CPU最近),所以你遍历数据的方式是最快的,这就是简单数据结构的优势
Disputor--要解决锁和缓存缺失带来的性能开销问题..采用了环形数据结构来解决这个问题(他们称之为魔法圆环,或魔法圆圈之类的),此种数据结构的有点是,不需要记录额外的下标,直接由JNI返回可以操作的地址,然后当多线程并发读写的时候,使用的也是cas方式。需要特别指出的是,这里所说的队列是系统内部的内存队列,而不是Kafka这样的分布式队列
队列的底层一般分成三种:数组、链表和堆。其中,堆一般情况下是为了实现带有优先级特性的队列,暂且不考虑。
通过不加锁的方式实现的队列都是无界的(无法保证队列的长度在确定的范围内);而加锁的方式,可以实现有界队列。在稳定性要求特别高的系统中,为了防止生产者速度过快,导致内存溢出,只能选择有界队列;同时,为了减少Java的垃圾回收对系统性能的影响,会尽量选择array/heap格式的数据结构。这样筛选下来,符合条件的队列就只有ArrayBlockingQueue。
ArrayBlockingQueue在实际使用过程中,会因为加锁和伪共享等出现严重的性能问题,
L1、L2、L3分别表示一级缓存、二级缓存、三级缓存,越靠近CPU的缓存,速度越快,容量也越小。所以L1缓存很小但很快,并且紧靠着在使用它的CPU内核;L2大一些,也慢一些,并且仍然只能被一个单独的CPU核使用;L3更大、更慢,并且被单个插槽上的所有CPU核共享;最后是主存,由全部插槽上的所有CPU核共享。
loggers all async采用的是Disruptor,而Async Appender采用的是ArrayBlockingQueue队列。
以上为随便剪切的片段 反正就是Disputor 牛逼 导致log4j2 牛逼