03 | 字符串性能优化不容小觑,百M内存轻松存储几十G数据
- String对象是如何实现的?
- String对象的不可变性
- String对象的优化
- 1.如何构建超大字符串?
- 2.如何使用
String.intern
节省内存? - 3.如何使用字符串的分割方法?
04 | 慎重使用正则表达式
05 | ArrayList还是LinkedList?使用不当性能差千倍
-
初识List接口
-
ArrayList是如何实现的?
- 问题1:我们在查看ArrayList的实现类源码时,你会发现对象数组elementData使用了transient修饰,我们知道transient关键字修饰该属性,则表示该属性不会被序列化,然而我们并没有看到文档中说明ArrayList不能被序列化,这是为什么?
- 问题2:我们在使用ArrayList进行新增、删除时,经常被提醒“使用ArrayList做新增删除操作会影响效率”。那是不是ArrayList在大量新增元素的场景下效率就一定会变慢呢?
- 问题3:如果让你使用for循环以及迭代循环遍历一个ArrayList,你会使用哪种方式呢?原因是什么?
- 1.ArrayList实现类
- 2.ArrayList属性
- 3.ArrayList构造函数
- 4.ArrayList新增元素
- 5.ArrayList删除元素
- 6.ArrayList遍历元素
-
LinkedList是如何实现的?
- 1.LinkedList实现类
- 2.LinkedList属性
- 3.LinkedList新增元素
- 4.LinkedList删除元素
- 5.LinkedList遍历元素
-
1.ArrayList和LinkedList新增元素操作测试
-
2.ArrayList和LinkedList删除元素操作测试
-
3.ArrayList和LinkedList遍历元素操作测试
06 | Stream如何提高遍历集合效率?
- 什么是Stream?
- Stream如何优化遍历?
-
1.Stream操作分类
-
2.Stream源码实现
-
3.Stream操作叠加
-
4.Stream并行处理
-
合理使用Stream
-
测试性能比较:在串行处理操作中,在并行处理操作中。
07 | 深入浅出HashMap的设计与优化
- 常用的数据结构
- 数组
- 链表
- 哈希表
- 树
- HashMap的实现结构
- HashMap的重要属性
- HashMap添加元素优化
- HashMap获取元素优化
- HashMap扩容优化
08 | 网络通信优化之I/O模型:如何解决高并发下I/O瓶颈?
- 什么是I/O: I/O是机器获取和交换信息的主要渠道,而流是完成I/O操作的主要方式。
- 1.字节流
- 2.字符流
“不管是文件读写还是网络发送接收,信息的最小存储单元都是字节,那为什么I/O流操作要分为字节流操作和字符流操作呢?”
答:我们知道字符到字节必须经过转码,这个过程非常耗时,如果我们不知道编码类型就很容易出现乱码问题。所以I/O流提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。
- 传统I/O的性能问题
- 1.多次内存复制
- 2.阻塞
- 如何优化I/O操作
- 1.使用缓冲区优化读写流操作
- 2.使用DirectBuffer减少内存复制
- 3.避免阻塞,优化I/O操作
- 1.多次内存复制
- 通道(Channel)
- 多路复用器(Selector)
09 | 网络通信优化之序列化:避免使用Java序列化
两个服务之间要共享一个数据对象,就需要从对象转换成二进制流,通过网络传输,传送到对方服务,再转换回对象,供服务方法调用。这个编码和解码过程我们称之为序列化与反序列化。
在大量并发请求的情况下,如果序列化的速度慢,会导致请求响应时间增加;而序列化后的传输数据体积大,会导致网络吞吐量下降。所以一个优秀的序列化框架可以提高系统的整体性能。
-
Java序列化
- Java序列化的缺陷
- 1.无法跨语言
- 2.易被攻击 ----> 那么后来是如何解决这个漏洞的呢?
- 3.序列化后的流太大
- 4.序列化性能太差
-
使用Protobuf序列化替换Java序列化
目前业内优秀的序列化框架有很多,而且大部分都避免了Java默认序列化的一些缺陷。例如,最近几年比较流行的FastJson、Kryo、Protobuf、Hessian
等。我们完全可以找一种替换掉Java序列化,这里我推荐使用Protobuf序列化框架。 Protobuf序列化原理
- 这里拓展一点,我来讲下什么是Protocol Buffers存储格式以及它的实现原理。 具体看原文
- Protobuf定义了一套自己的编码方式,几乎可以映射Java/Python等语言的所有基础数据类型。不同的编码方式对应不同的数据类型,还能采用不同的存储格式。如下图所示:
10 | 网络通信优化之通信协议:如何优化RPC网络通信?
-
RPC通信是大型服务框架的核心
-
我认为微服务的核心是远程通信和服务治理。
目前,很多微服务框架中的服务通信是基于RPC通信实现的,在没有进行组件扩展的前提下,SpringCloud
是基于Feign组件实现的RPC通信(基于Http+Json序列化实现),Dubbo
是基于SPI扩展了很多RPC通信框架,包括RMI、Dubbo、Hessian等RPC通信框架(默认是Dubbo+Hessian序列化)。
不同的业务场景下,RPC通信的选择和优化标准也不同。例如,开头我提到的我们部门在选择微服务框架时,选择了Dubbo。当时的选择标准就是RPC通信可以支持抢购类的高并发,在这个业务场景中,请求的特点是瞬时高峰、请求量大和传入、传出参数数据包较小。而Dubbo中的Dubbo协议就很好地支持了这个请求。
-
什么是RPC通信
RMI(Remote Method Invocation)
是JDK中最先实现了RPC通信的框架之一,RMI的实现对建立分布式Java应用程序至关重要,是Java体系非常重要的底层技术,很多开源的RPC通信框架也是基于RMI实现原理设计出来的,包括Dubbo框架中也接入了RMI框架。接下来我们就一起了解下RMI的实现原理,看看它存在哪些性能瓶颈有待优化。 -
RMI:JDK自带的RPC通信框架
- RMI的实现原理
- RMI在高并发场景下的性能瓶颈
- Java默认序列化
- TCP短连接
- 阻塞式网络I/O
-
一个高并发场景下的RPC通信优化路径
- 1.选择合适的通信协议
- 2.使用单一长连接
- 3.优化Socket通信
- 实现非阻塞I/O
- 高效的Reactor线程模型
- 串行设计
- 零拷贝
除了以上这些优化,我们还可以针对套接字编程提供的一些TCP参数配置项,提高网络吞吐量,Netty可以基于ChannelOption来设置这些参数。
- 4.量身定做报文格式
- 5.编码、解码
- 6.调整Linux的TCP参数设置选项
在一些并发场景比较多的系统中,我更偏向使用Dubbo
实现的这一套RPC通信协议。Dubbo协议是建立的单一长连接通信,网络I/O为NIO非阻塞读写操作,更兼容了Kryo、FST、Protobuf等性能出众的序列化框架,在高并发、小对象传输的业务场景中非常实用。
11 | 答疑课堂:深入了解NIO的优化实现原理
Tomcat中经常被提到的一个调优就是修改线程的I/O模型。Tomcat 8.5版本之前,默认情况下使用的是BIO线程模型,如果在高负载、高并发的场景下,可以通过设置NIO线程模型,来提高系统的网络通信性能。
网络通信中,最底层的就是内核中的网络I/O模型了。随着技术的发展,操作系统内核的网络模型衍生出了五种I/O模型,《UNIX网络编程》一书将这五种I/O模型分为阻塞式I/O、非阻塞式I/O、I/O复用、信号驱动式I/O和异步I/O。每一种I/O模型的出现,都是基于前一种I/O模型的优化升级。
-
网络I/O模型优化
- 那阻塞到底发生在套接字(socket)通信的哪些环节呢?
-
1.阻塞式I/O
- connect阻塞
- accept阻塞
- read、write阻塞
-
2.非阻塞式I/O
使用fcntl可以把以上三种操作都设置为非阻塞操作
。如果没有数据返回,就会直接返回一个EWOULDBLOCK或EAGAIN错误,此时进程就不会一直被阻塞。 -
3.I/O复用
如果使用用户线程轮询查看一个I/O操作的状态,在大量请求的情况下,这对于CPU的使用率无疑是种灾难。 那么除了这种方式,还有其它方式可以实现非阻塞I/O套接字吗?
Linux提供了I/O复用函数select/poll/epoll,进程将一个或多个读操作通过系统调用函数,阻塞在函数操作上。这样,系统内核就可以帮我们侦测多个读操作是否处于就绪状态。- select()函数
- poll()函数
- epoll()函数
- 通过以上代码,我们可以看到:epoll_ctl()函数中的epfd是由 epoll_create()函数生成的一个epoll专用文件描述符。op代表操作事件类型,fd表示关联文件描述符,event表示指定监听的事件类型。
-
4.信号驱动式I/O
-
5.异步I/O
-
零拷贝
- 线程模型优化
- 1.单线程Reactor线程模型
- 2.多线程Reactor线程模型
- 3.主从Reactor线程模型
- 基于线程模型的Tomcat参数调优