《JAVA程序性能优化》总结
设计优化
- 单例模式:延时加载(内部类)。反射和序列化会破坏单例
- 代理模式:延时加载
- 静态代理:包括主题接口、真实主题、代理类、main。初始化时使用代理,真正使用时再通过代理加载真实主题。
- 动态代理:jdk动态代理、CGLIB、javassist基于动态代码的代理、ASM。
- 动态加载过程(以CGLIB为例):
- 根据指定的回调类生成class字节码,并保存在byte数组中。
- 通过反射,调用ClassLoader.defindClass将字节码定义为类。
- 使用反射机制生成该类的实例。
- 经典实现:AOP。AOP的实现方式有以下几种方式:
- 静态织入:在编译期将切面织入到目标文件中。
- jdk动态代理:运行期为接口生成代理类,再将切面织入代理类中。
- CGLIB和javassist动态字节码:运行期,在目标加载后,动态构建字节码文件生成目标类的子类,将切面织入子类中。
- 享元模式:
- 复用大对象,节省内存和创建的时间。
- 和对象池的不同在于:享元对象都是不同的,各自有各自的含义和用途,而对象池的对象都是等价的,如数据库连接池中的连接。
- 装饰者模式:
- 可以将功能组件和性能组件分开,需要再结合起来。
- 设计原则:使用委托,少用继承。
- 经典例子:OutputStream和InputStream。
- 观察者模式
常用优化组件和方法
- 缓冲:
- 协调上层组件和下层组件的性能差异,最常用于提高I/O的速度。
- 经典例子:写文件操作,FileWriter和BufferedWriter,使用缓冲区的writer性能会提升一倍。
- 缓存
- 对象复用—池
- 只有对重量级对象使用对象池技术才能提高系统性能,对轻量级对象使用对象池,反而会降低系统性能。
- 在Apache中,已经提供了一个Jakarta Commons Pool对象池组件可使用。
- 并行替代串行:数据迁移
- 负载均衡
- 时间换空间:CPU与内存
- 空间换时间:缓存
java程序优化
- String:
- 由char数组、偏移量和string长度组成。
- 其真实长度由偏移量和长度在这个char数组中进行定位和截取。
- substring容易导致内存泄漏。
- 对于字符串相加,如果直接使用string相加,编译期会自动优化成StringBuilder,所以会有所优化。但若是在for循环中string相加,编译期没有那么只能,所以每次循环都会为string创建一个新的StringBuilder,所以相对而言,直接用string相加还是不如直接用StringBuilder来append。
- List(ArrayList、Vector、LinkedList):
- ArrayList和Vector —> AbstractList —> List。
LinkedList —> AbstractSequenceList —> AbstractList —> List。 - ArrayList和Vector都是基于数组实现,封装了对内部数组的操作。
LinkedList使用循环双向链表数据结构。前驱表项(最后一个元素) <— 当前元素(header) —> 后驱表项(第一个元素)。 - 普通新增操作(即直接新增到最后)。
//ArrayList
public boolean add(E e){
encureCapacity(size+1);//确保内部数组有足够空间,不够则进行扩容,1.5倍。性能取决于这个方法
elementData[size++] = e;//将元素加入到数组的末尾,完成添加
return true;
}
//LinkedList
private Entry(E) addBefore(E e, Entry entry){
//下面三行代码是性能消耗的关键点
Entry newEntry = new Entry(e, entry, entry.previous);//创建新的元素
newEntry.previous.next = newEntry;//将前驱表项的下一个元素指向当前新增元素
newEntry.next.previous = newEntry;//将后驱表项的上一个元素指向当前新增元素
size++;
modCount++;
return newEntry;
} - 根据下标新增到指定位置
ArrayList:每次插入操作,都会进行数组复制,下标越前,性能越差。
LinkedList:在哪里插入性能都一样。 - 删除指定位置
ArrayList:同新增一样,都需要进行数组的复制,从尾到头性能逐渐提升。
LinkedList:需要遍历,下标为中间是性能最差,需要遍历1/2的列表元素。 - foreach运行时会被编译期解析成迭代器,反编译的代码看到还多了一步赋值的操作,所以三种循环的性能排序为for循环 > 迭代器 > foreach。
- ArrayList和Vector —> AbstractList —> List。
- Map:
- properties —> HashTable —> Dictionary、Map。
HashMap —> AbstractMap —> Map。
TreeMap —> AbstractMap —> Map。
LinkedHashMap —> HashMap —> AbstractMap —> Map。 - HashTable不允许key或value使用null值,但HashMap可以。
- HashMap原理:将key做hash算法,然后将hash值映射到内存地址,直接取得key所对应的数据。底层数据结构是数组,内存地址即数组的下标索引。
- 为何HashMap是高性能的:
- hash算法高效(多采用navive本地方法和位运算)。
- hash值到内存地址(数组索引)的算法高效(根据hash值和数组长度 按位与计算)。
- 根据内存地址(数组索引)可以直接取得对应的值。
- hash算法和查找的源码:
- hash算法:
int hash = hash(key.hashCode());//计算key的hash值
public native int hashCode();//可以重写,性能关键点,所以重写的hashCode方法是否冲突很重要
static int hash(itn h){//基于位运算
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
} - 查找内存地址的算法:
int i = indexFor(hash, table.length);
static int indexFor(int h, int length){
return h & (length-1);
}
- hash算法:
- HashMap高性能的注意条件:
- hashCode()方法的实现,要尽可能减少冲突,这样子对HashMap的操作就近乎对数组的随机访问。若冲突多,则相当于退化成几个链表,等价于遍历链表,性能很差。一般hashCode的生成可以直接使用eclipse IDE提供的方法,或者引入第三方库如Apache commons来生成。
- 容量参数,HashMap的扩容会遍历整个HashMap,对里面的数据进行重新计算在新数组的位置,所以应尽量避免扩容,在初始化的时候就估算好大概的容量。
- 负载因子 = 元素个数 / 内部数组大小,默认为0.75,尽量不要大于1,这样子会带来冲突。
- HashMap的表项结构,实际上是一个链表的数组。
Entry1(每个Entry包括key、value、next、hash)
Entry2
…
…
Entryn —> Entryn1 —> Entryn2(hash冲突的链表结构)。 - LinkedHashMap:维护了元素次序表的HashMap,在每个Entry对象中添加了before、after两个属性。有两种排序类型,按照元素进入集合的顺序或者被访问的先后顺序排序。
- TreeMap:基于匀速的固有顺序排序(由Comparator或者Comparable确定)。其内部实现是基于红黑树,是一种平衡查找树,性能要优于平衡二叉树,可以在O(log n)时间内查找、插入和删除。
- properties —> HashTable —> Dictionary、Map。
- Set:
HashSet、LinkedHashSet、TreeSet都只是对应的Map的一种封装,所有操作都委托给HashMap对象完成。 - NIO:
- 与流式I/O不同,它是基于块(Block)的。
- 最重要的两个组件:缓冲Buffer和通道Channel。通道表示缓冲数据的源头或目的地,用于向缓冲读取或者写入数据,是访问缓冲的接口。应用程序不能直接对Channel进行读写操作,而必须通过Buffer来进行。
- NIO进行文件复制的例子:
public static void nioCopyFile(String resource, String destination){
FileInputStream fis = new FileInputStream(resource);
FileOutputStream fos = new FileOutputStream(destination);
FileChannel readChannel = fis.getChannel();//读文件通道
FileChannel writeChannel = fos.getChannel();//写文件通道
ByteBuffer buffer = ByteBuffer.allocate(1024);//读入数据缓存
while(true){
buffer.clear();
int len = readChannel.read(buffer);
if(len == 1){
break;
}
buffer.flip();
writerChannel.write(buffer);
}
readChannel.close();
writeChannel.close();
} - Buffer3个重要的参数:position(位置)、capacity(容量)、limit(上限)。当执行flip()操作会将写模式转换为读模式,并将limit设置为当前position和将position置为0。
- 三种文件流操作性能对比:基于Buffer的性能比普通的基于流的性能要高一倍,基于Buffer并将文件映射到内存的性能要高出一个数量级。
并行程序开发及优化
- Future模式(可用于一个大方法中,某些小方法比较耗时,则可以将这些小方法用这种模式处理)
- 核心在于去除主函数等待时间,并使得原本需要等待的时间段可以用于处理其他业务,充分利用计算机资源。
- jdk的并发包中已经内置了一种Future模式的实现,关键是callable接口的call()方法,重载定义业务逻辑。
- Master-Worker模式(可用于数据迁移前的准备数据,多进程收集数据并异步计算结果)
- Master进程为主要进程,维护了一个worker进程队列、子任务队列和子结果集。Worker进程队列中的worker进程不停地从任务队列中提取要处理的子任务,并将子任务的处理结果写入结果集。
- 可以使用ForkJoinPool框架。
- 生产者-消费者模式
- 模式架构图:生产者 —> 内存缓冲区 —> 消费者。
- 生产者Producer:用于提交用户请求,提取用户任务,并装入内存缓冲区。
消费者Consumer:在内存缓冲区中提取并处理任务。
内存缓冲区BlockingQueue:缓存生产者提交的任务或数据,供消费者使用。
《JAVA程序性能优化》总结相关推荐
- STL源码剖析学习七:stack和queue
STL源码剖析学习七:stack和queue stack是一种先进后出的数据结构,只有一个出口. 允许新增.删除.获取最顶端的元素,没有任何办法可以存取其他元素,不允许有遍历行为. 缺省情况下用deq ...
- 《STL源码剖析》学习-- 1.9-- 可能令你困惑的C++语法1
最近在看侯捷的<STL源码剖析>,虽然感觉自己c++看得比较深一点,还是感觉还多东西不是那么明白,这里将一些细小的东西或者概念记录一下. 有些东西是根据<C++编程思想>理解的 ...
- 《STL源码剖析》学习--6章--_rotate算法分析
最近在看侯捷的<STL源码剖析>,其中有许多不太明白之处,后经分析或查找资料有了些理解,现记录一下. <STL源码剖析>学习--6章--random access ite ...
- 《STL源码剖析》学习--6章--power算法分析
最近在看侯捷的<STL源码剖析>,其中有许多不太明白之处,后经分析或查找资料有了些理解,现记录一下. 6章--power算法分析 书本中的算法如下所示: template <clas ...
- STL源码剖析——P142关于list::sort函数
在list容器中,由于容器自身组织数据的特殊性,所以list提供了自己的排序函数list::sort, 并且实现得相当巧妙,不过<STL源码剖析>的原文中,我有些许疑问,对于该排序算法,侯 ...
- STL源码剖析---红黑树原理详解下
转载请标明出处,原文地址:http://blog.csdn.net/hackbuteer1/article/details/7760584 算法导论书上给出的红黑树的性质如下,跟STL源码 ...
- STL源码剖析面试问题
当vector的内存用完了,它是如何动态扩展内存的?它是怎么释放内存的?用clear可以释放掉内存吗?是不是线程安全的? vector内存用完了,会以当前size大小重新申请2* size的内存,然后 ...
- STL源码剖析学习二:空间配置器(allocator)
STL源码剖析学习二:空间配置器(allocator) 标准接口: vlaue_type pointer const_pointer reference const_reference size_ty ...
- STL源码剖析 数值算法 copy 算法
copy复制操作,其操作通过使用assignment operator .针对使用trivial assignment operator的元素型别可以直接使用内存直接复制行为(使用C函数 memove ...
- STL源码剖析 算法开篇
STL源码剖析 算法章节 算法总览_CHYabc123456hh的博客-CSDN博客 质变算法 质变算法 - 会改变操作对象的数值,比如互换.替换.填写.删除.排列组合.分隔.随机重排.排序等 #in ...
最新文章
- ZServer4D开源项目
- 区块链技术没那么复杂,别被大佬们忽悠晕了
- php sql注入 替换,通过替换单引号来防止SQL注入
- matlab中服从高斯分布的矩阵_推荐基础算法之矩阵分解PMF
- python 将元组解析为多个参数
- 微信小程序开发:学习笔记[8]——页面跳转及传参
- pytorch:固定部分层参数,固定单个模型
- 质量属性效用树例子_数百个 HTML5 例子学习 HT 图形组件 – 拓扑图篇
- 兰空图床Lsky Pro 免费开源图床网站程序v1.6.3
- tcp与udp的socket编程,udp代码实现客户端服务端,tcp代码实现客户端服务端(单线程、多线程、多进程分别实现),三次握手
- SSH2整合需要jar包解释
- ETH 5分钟内涨幅2.06%,现价1104.03usdt
- Java 方法的反射
- keil 之Vscode编辑器插件、格式化代码插件和最新注册机分享
- 前端知识:使用layui模板搭建页面,简单快捷
- 树上随机游走的期望距离
- android设备内部添加apn信息
- python编写五子棋小游戏 (电脑自走棋)
- 【OpenCV】纹理提取
- APP启动时白屏/黑屏 或者 Activity打开时白屏/黑屏