Java性能 -- 正则表达式
元字符
正则表达式使用一些特定的元字符来检索、匹配和替换符合规则的字符串
元字符:普通字符、标准字符、限定字符(量词)、定位字符(边界字符)
正则表达式引擎
正则表达式是一个用正则符号写出来的公式
程序对正则表达式进行语法分析,建立语法分析树
再根据语法分析树结合正则表达式引擎生成执行程序(状态机),用于字符匹配
正则表达式引擎是一套核心算法,用于建立状态机
小结
正则表达式 => 语法分析树
语法分析树 + 正则表达引擎 => 状态机 => 用于字符匹配
目前实现正则表达式引擎的方式有两种
DFA自动机(Deterministic Finite Automaton,确定有限状态自动机)
NFA自动机(Nondeterministic Finite Automaton,非确定有限状态自动机)
DFA自动机的构造代价远大于NFA自动机,但DFA自动机的执行效率高于NFA自动机
假设一个字符串的长度为n,如果采用DFA自动机作为正则表达式引擎,则匹配的时间复杂度为O(n)
如果采用NFA自动机作为正则表达式引擎,NFA自动机在匹配过程中存在 ...
Kafka -- 术语
主题 + 客户端
发布订阅的对象是主题(Topic)
向主题发布消息的客户端应用程序称为生产者(Producer),生产者可以持续不断地向多个主题发送消息
订阅这些主题消息的客户端应用程序称为消费者(Consumer),消费者能够同时订阅多个主题的消息
生产者和消费者统称为客户端
服务端
Kafka的服务端由被称为Broker的服务进程构成,一个Kafka集群由多个Broker组成
Broker负责接收和处理客户端发送过来的请求,以及对消息进行持久化
多个Broker进程能够运行在同一台机器上,但更常见的做法是将不同的Broker分散运行在不同的机器上
这样如果集群中某一台机器宕机了,即使在它上面运行的所有Broker进程都挂掉了
其他机器上的Broker也依然能够对外提供服务,这是Kafka提供高可用的手段之一
备份
实现高可用的另一个手段是备份机制(Replication)
备份:把相同的数据拷贝到多台机器上,这些相同的数据拷贝在Kafka中被称为副本(Replica)
副本的数量是可以配置的,Kafka定义了两类副本:领导者副本(Leader Replica)和追随者副本(Follower ...
Kafka -- 消息引擎系统
术语
Apache Kafka是一款开源的消息引擎系统
消息队列:给人某种暗示,仿佛Kafka是利用队列实现的
消息中间件:过度强调中间件,而不能清晰地表达实际解决的问题
解决的问题
系统A发送消息给消息引擎系统,系统B从消息引擎系统中读取A发送的消息
消息引擎传输的对象是消息
如何传输消息属于消息引擎设计机制的一部分
消息格式
成熟解决方案:CSV、XML、JSON
序列化框架:Google Protocol Buffer、Facebook Thrift
Kafka:纯二进制的字节序列
消息引擎模型
点对点模型
即消息队列模型,系统A发送的消息只能被系统B接收,其他任何系统不能读取A发送的消息
发布订阅模型
主题(Topic)、发布者(Publisher)、订阅者(Subscriber)
多个发布者可以向相同的主题发送消息,多个订阅者可以接收相同主题的消息
Kafka同时支持上面两种消息引擎模型
JMS
JMS:Java Message Service
JMS也支持上面的两种消息引擎模型
JMS并非传输协议,而是一组API
JMS非常出名,很多主流的消息引擎系统都支持JMS规范
Acti ...
Java并发 -- CSP模型
Go
Go是一门号称从语言层面支持并发的编程语言,支持并发也是Go非常重要的特性之一
Go支持协程,协程可以类比Java中的线程,解决并发问题的难点在于线程(协程)之间的协作
Go提供了两种方案
支持协程之间以共享内存的方式通信,Go提供了管程和原子类来对协程进行同步控制,该方案与Java类似
支持协程之间以消息传递的方式通信,本质上是要避免共享,该方案是基于CSP模型实现的,Go推荐该方案
CSP模型
CSP:Communicating Sequential Processes
Do not communicate by sharing memory; instead, share memory by communicating.
累加器12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455package mainimport ( "fmt" "time")func main() { ...
Java性能 -- 字符串
实现
在Java 6以及之前的版本中,String对象是对char数组进行了封装实现的对象
主要四个成员变量:char数组、偏移量offset、字符数量count、哈希值hash
String对象通过offset和count两个属性来定位char数组,获取字符串
这样可以高效快速地共享数组对象,同时节省内存空间,但也有可能会导致内存泄露
Java 7/8,String类中不再有offset和count两个变量
这样String对象占用的内存稍微少了一点
另外,String.substring不再共享char[],从而解决了使用该方法可能导致的内存泄露问题
从Java 9开始,将char[]修改为byte[],同时维护了一个新的属性coder,它是一个编码格式的标识
一个char字符占用16位,2个字节,用一个char去存储单字节编码的字符会非常浪费
Java 9的String类为了节约内存空间,使用了占用8位,1个字节的byte数组来存放字符串
coder的作用:使用length()或者indexOf()
coder有两个默认值:0代表LATIN1,1代表UTF16
不 ...
Java并发 -- 协程
协程
协程可以理解为一种轻量级的线程
从操作系统的角度来看,线程是在内核态中调度的,而协程是在用户态调度的,协程的切换成本更低
协程栈比线程栈要小得多,典型的线程栈在1M左右,而协程栈一般在几K或者几十K左右
因此无论在时间维度还是在空间维度,协程都比线程轻量很多
支持协程的语言:Go、Python、Lua、Kotlin
Java OpenSDK的Loom项目的目标是为了支持协程
Go中的协程12345678910func hello(msg string) { fmt.Println("Hello " + msg)}func TestCoroutine(t *testing.T) { // 在新的协程中执行hello方法 go hello("Go") // 等待100毫秒让协程执行结束 time.Sleep(100 * time.Millisecond)}
Java中的线程是一个重量级对象,因此无法很好地实现Thread-Per-Message模式,而协程可以
Thread-Per-Me ...
Java并发 -- 软件事务内存
STM
STM:Software Transactional Memory,软件事务内存,借鉴于数据库的事务管理
传统的数据库事务支持ACID,即原子性(A)、一致性(C)、隔离性(I)和持久性(D)
STM不支持持久化,即只支持ACI
数据库事务数据库保证在并发情况下不会发生死锁,而且还能保证ACID
1234567891011121314Connection conn = null;try{ // 获取数据库连接 conn = DriverManager.getConnection(); // 设置手动提交事务 conn.setAutoCommit(false); // 执行转账SQL ... // 提交事务 conn.commit();} catch (Exception e) { // 出现异常回滚事务 conn.rollback();}
synchronized转账12345678910111213141516@AllArgsConstructorpublic class UnsafeAcco ...
Java性能 -- 性能调优策略
性能测试测试方法
微基准性能测试
可以精准定位到某个模块或者某个方法的性能问题,例如对比一个方法使用同步实现和非同步实现的性能差异
宏基准性能测试
宏基准性能测试是一个综合测试,需要考虑到测试环境、测试场景和测试目标
测试环境:模拟线上的真实环境
测试场景:在测试某个接口时,是否有其他业务的接口也在平行运行,进而造成干扰
测试目标
可以通过吞吐量和响应时间来衡量系统是否达标,如果不达标,就需要进行优化
如果达标,就继续加大测试的并发数,探底接口的TPS
除了关注接口的吞吐量和响应时间外,还需要关注CPU、内存和IO的使用率情况
干扰因素热身问题
在Java编程语言和环境中,.java文件编译成.class文件后,需要通过解析器将字节码转换成本地机器码才能运行
为了节约内存和执行效率,代码在最初被执行时,解析器会率先解析执行这段代码
随着代码被执行的次数增加,当JVM发现某个方法或代码块运行得很频繁时,就会把这些代码认定为热点代码
为了提高热点代码的执行效率,在运行时,JVM将通过即时编译器(JIT)把这些代码编译成与本地平台相关的机器码
并进行各层次的优化,然后存储在内存中,之后每次运行代码 ...
Java并发 -- Actor模型
Actor模型
Actor模型在本质上是一种计算模型,基本的计算单元称为Actor,在Actor模型中,所有的计算都在Actor中执行
在面向对象编程里,一切都是对象,在Actor模型里,一切都是Actor,并且Actor之间是完全隔离的,不会共享任何变量
Java本身并不支持Actor模型,如果需要在Java里使用Actor模型,需要借助第三方类库,比较完备的是Akka
Hello Actor12345678910111213141516public class HelloActor extends UntypedAbstractActor { // 该Actor在收到消息message后,会打印Hello message @Override public void onReceive(Object message) throws Throwable { System.out.printf("Hello %s%n", message); } public static void main(String[] a ...
Java性能 -- 性能调优标准
性能瓶颈
CPU
如果应用需要大量计算,会长时间占用CPU资源,导致其它应用因无法争夺到CPU而响应缓慢
场景:代码递归导致的无限循环,JVM频繁的Full GC、多线程编程造成的大量上下文切换
内存
Java程序一般通过JVM对内存进行分配管理,主要使用JVM中的堆内存来存储Java创建的对象
但内存空间有限,当内存空间被占满,对象无法回收,会导致内存溢出,内存泄露等问题
磁盘IO
网络:带宽
异常:Java应用中,抛出异常需要构建异常栈,对异常进行捕获和处理,这个过程非常消耗系统性能
数据库:数据库的操作往往涉及到磁盘IO的读写,大量的数据库读写操作,会导致磁盘IO的性能瓶颈
锁竞争
在并发编程中,经常需要使用到多线程,并发读写同一个共享资源,为了保证数据原子性,会用到锁
锁的使用会带来上下文切换,从而给系统带来性能开销
性能指标响应时间
一个接口的响应时间一般在毫秒级
数据库响应时间:数据库操作所消耗的时间,往往是整个请求链中最耗时的
服务端响应时间:包括Nginx分发请求所消耗的时间以及服务端程序执行所消耗的时间
网络响应时间:在网络传输时,网络硬件对需要传输的请求进行解析等操作 ...