# 2.2 常用优化组件和方法

# 2.2.1 缓冲(Buffer)

缓冲区是一块的定的内存区域。开辟缓冲区的目的是通过缓解应该程序上下层之间的性能差异,提高系统的性能。在日常生活中,缓冲的一个典型应用是漏斗,如图2.19所示:

图2.19显示了漏斗作为缓冲区的使用场景。上层系统如茶壶,下层系统如水瓶。现需要将茶壶中的水倒入水瓶中,这就有如将内存中的数据写入硬盘中一样。茶壶的出水速度可以很快,但是水瓶的瓶口很细,因此形成性能瓶颈。要将水全部倒入瓶中,必须等待瓶口的水缓缓流下。为了加快速度,可以使用一个漏斗(缓冲).

漏斗的初始口径很大,并且拥有一定的容量,因此,茶壶中的水可以先倒入漏斗中,就犹如内存数据先写入一块缓冲区。只要漏斗的容量够大,茶壶里的水很快就能倒完。至此,上层系统完成工作,可以去处理其他业务逻辑。而此时,水并未完全进入瓶中,而大部分被积累在漏斗中。就可以由下层系统慢慢处理,直到水完全进入瓶中,漏斗(缓冲区)被清空。

注意:缓冲可以协调上层组件和下层组件的性能差。当上层组件性能优于下层组件时,可以有效减少上层组件对下层组件的等待时间。

基于这样的结果,上层应用组件不需要等待下层组件真实的接收全部数据,即可返回操作,加快了上层组件的处理速度,从而提升系统整体性能。

缓冲最常用的场景就是提高I/O的速度。为此,JDK内不少I/O组件都提供了缓冲功能。比如当使用FileWriter时,进行文件写操作的代码如下:

Writer writer = new FileWriter(new File("file.txt"));
long begin = System.currentTimeMillis();
for(int i=0;i<CIRCLE;i++){
    writer.write(i);
}
writer.close;
System.out.println("testFileWriter speed:"+(System.currentTimeMillis()-begin));

为进行I/O优化,可以为FileWriter加上缓冲:

Writer writer = new BufferedWriter(new FileWriter(new File("file.txt")));
long begin = System.currentTimeMillis();
for(int i=0;i<CIRCLE;i++){
    writer.write(i);
}
writer.close;
System.out.println("testFileWriter speed:"+(System.currentTimeMillis()-begin));

以上代码使用BufferedWriter 为 FileWriter 对象增加缓冲功能。BufferedWriter对象拥有两个构造方法:

public BufferedWriter(Writer out)

public BufferedWriter(Writer out,int size)

其中,第2个构造函数允许再应用层指定缓冲区的大小,第一个构造函数将构造大小为8K 的缓冲区。一般来说,缓冲区不宜过小,过小的缓冲区无法骑到真正的缓冲作用,缓冲区也不宜过大,过大的缓冲区会浪费系统内存,增加GC负担。本例中,设置循环次数CIRCLE为10万,若不使用缓冲区操作,则相对耗时62ms,而使用缓冲区的FileWriter近相对耗时32ms,性能提升一倍。

另一个有用的缓冲组件是BufferedOutputStream 。在前文“装饰者”中,已经提到,使用BufferedOutputStream可以包装所有的OutputStream,为其提供缓冲功能,提高输出流的效率。和BufferedWriter类似,它也提供了两个构造函数:

public BufferedOutputStream(OutputStream out)

public BufferedOutputStream(OutputStream out,int size)

第2个构造函数可以指定缓冲区大小,默认情况下,和BufferedWriter一样,缓冲区大小为8K。

除了能够改善I/O性能,缓冲区对任何一种上下层组件存在性能差异的场合都可以起到很好的效果。

# 2.2.2 缓存(Cache)

# 2.2.3 对象复用——“池”

# 2.2.4 并行替代串行

# 2.2.5 负载均衡

# 2.2.6 时间换空间

# 2.2.7 空间换时间

Last Updated: 12/15/2023, 8:18:50 AM