Java性能优化
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
• Object obj表示一个本地引用,存储在JVM栈的本地变 量表中,表示一个reference类型数据;
• new Object()作为实例对象数据存储在堆中; • 堆中还记录了Object类的类型信息(接口、方法、fiel
d、对象类型等)的地址,这些地址所执行的数据存储在 方法区中; 在Java虚拟机规范中,对于通过reference类型引用访问具体 对象的方式并未做规定,目前主流的实现方式主要有两种:
IO的选择和使用
• BIO: 同步、阻塞
一个连接一个 线程
1. 优点:代码简单、直观 2. 缺点:IO效率、扩展性存在局限性,会成为应用性能的瓶颈
IO的选择和使用
• NIO:同步、非阻塞
一个请求一个线程
1.可以构建多路复用的、同步非阻塞 IO 程序 2.同时提供了更接近操作系统底层的高性能数据操作方式。
网络IO
• 百度文库多时候大家都容易忽略网络对系统的影响,实际上网络带宽在一些情况 下也会成为系统的瓶颈。一旦在业务的请求和响应中包含较大的数据传输 时,往往会遇到网络瓶颈。因为更多的时候服务器采用的还是以太网卡, 1000M网卡在全双工模式下传输速率也只有80M/s,如果响应中包含报表、 图片之类的大尺寸数据,很有可能在性能测试中出现网络瓶颈。
如果指定了一个类为final,则该 类所有的方法都是final的。Java编译器会寻找机会 内联所有的final方法,内联对于提升Java运行效率作用重大。
确保唯一,不然就是子子孙孙无穷尽也
编码技巧
• 尽量重用对象
特别是String对象的使用,出现字符串连接时应该使用StringBuilder/StringBuffer 代替。由于Java虚拟机不仅要花时间生成对象,以后可能还需要花时间对这些对象进行 垃圾回收和处理,因此,生成过多的对象将会给程序的性能带来很大的影响。 (String对象改变一次,新生成一个对象)。集合里创建对象,同理
Key |
value
Map
• HashMap
Hash数组后边跟着小链表/红黑树
链表散列(1.7)
数组+链表+红黑树(1.8)
Map
• Hashtable
与 HashMap类似,不同的是:key和value的值均 不允许为null;它支持线程的同步,即任一时刻 只有一个线程能写Hashtable,因此也导致了
课程内容
性能瓶颈 代码层面
Jvm层面
编
结
io
异
参
回
码
构
流
常
数
收
家里厨房的水池下水堵 了,我们要找原因,首先 得知道水池的下水道都有 哪些部分:
简单的看,可以把下水道 分解为水漏、上连接管、 回水弯、下连接管最后接 入地漏。再查找堵塞位置 时,我们就可以将水直接 导入回水弯,排除水漏和 上连接管道堵塞的可能
选择技巧
• 可以借用set对list进行去重
• 使用最有效率的方式去遍历Map(entry)
代码优化
2Part •代码优化
1. 编码技巧 2. 数据结构的合理选择 3. IO的选择和使用
概念
先来个例子理解一下同步,阻塞的概念,以银行取款为例: • 同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读
编码技巧
• 尽量避免随意使用静态变量
private static B b = new B(); A类不被卸载,那么引.用B指向的B对象会常驻内存,直到程序终止。
• 公用的集合类中不使用的数据一定要及时remove掉(内 存泄漏的隐患)
• 静态类、单例类、工厂类将它们的构造函数置为private • 尽量使用System.arraycopy ()代替通过来循环复制数
HashSet不允许重复(HashMap的key不允许重复,如果出现重 复就覆盖),允许null值,非线程安全
Set
• HashSet内存泄漏
hash集合在操作不当的情况下,有可能造成内存泄漏。 考虑这样的情况,把一个对象存储进hashSet集合后,修改这个对象中参与计算hash的 变量的值,这时这个对象的hash值也会随之改变,那么这么对象可以正常地被删除吗?
• 还有一点就是不要忽略回环地址传输的影响,比如一些应用访问本地监听 的其他服务,都会受到网卡的传输速率限制的影响
磁盘IO
• 通常情况下,磁盘是计算机中速度最慢的一个子系统,因此很多情况中, 磁盘I/O会成为系统的瓶颈。实际上在设计高性能系统的时候,会把避免磁 盘I/O作为一个首要准则。
• 虽然当前的技术发展让存储系统的读写速度不断提升,但高昂的成本使得 大多数情况下,高速存储会使用在数据库或文件服务器上,而不会使用在 应用服务器中。所以在我们进行性能测试时,要更多的注意应用服务器的 磁盘使用情况。
常见性能瓶颈的识别
1 Part
•常见性能瓶颈的识别
1. CPU占有率 2. 内存占用 3. 网络IO 4. 磁盘IO
CPU占有率
• CPU的高占用,并不一定表示有问题,因为实现最优性能的一方面就是充 分发挥当前的硬件资源能力.但是CPU长期处于如图状态,就很值得我们关 注,至少说明在大多数情况下,系统已经是在耗用最大的计算能力进行计 算,运算能力已经成为瓶颈
否
Set
• TreeSet
TreeSet是基于TreeMap实现的。TreeSet中的元素支持2种排序方式:自然排序 或 者 根据创建TreeSet 时提供的 Comparator 进行排序。这取决于使用的构造方法。
TreeSet不支持快速随 机遍历
Map
独立于Collection存在的接口,如下图:(简约图)
对象的访问方式
通过句柄访问的实现方式中,JVM堆中会专门有一块区域用来 作为句柄池,存储相关句柄所执行的实例数据地址(包括在堆 中地址和在方法区中的地址)。这种实现方法由于用句柄表示 地址,因此十分稳定
对象的访问方式
通过直接指针访问的方式中,reference中存储的就是对象在 堆中的实际地址,在堆中存储的对象信息中包含了在方法区中 的相应类型数据。这种方法最大的优势是速度快,在HotSpot 虚拟机中用的就是这种方式
• 尽量在合适的场合使用单例
使用单例可以减轻加载的负担、缩短加载的时间、提高加载的效率,但并不是所有 地方都适用于单例,简单来说,单例主要适用于以下三个方面: (1)控制资源的使用,通过线程同步来控制资源的并发访问 (2)控制实例的产生,以达到节约资源的目的 (3)控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实 现通信
编码技巧
在了解对象访问方式和存储方式之后,我们就需要针对对象的存放和访问来书 写和规范我们的代码。
创建
java
面向对象
使用
减少创建,调高效率,及时销毁
销毁
编码技巧
• 尽量指定类、方法的final修饰符
带有final修饰符的类是不可派生(最后的,最终的)的,不可被继承,final修饰符 可以让方法不可以被重写。
IO的选择和使用
• NIO2/AIO:异步、非阻塞
1. 引入了异步非阻塞 IO 方式 2. 异步 IO 操作基于事件和回调机制—我们不需要像NIO编程那样创建一个独立的I/O
内存占用
• 内存在性能测试中是被重点关注的指标,因为它是反映重大缺陷——内存 泄露的最直接指标,但是我们应该注意到,在JAVA框架中的内存泄漏是发 生在虚拟内存中的。
• 观察内存/虚拟内存的占用情况,尤其是在压力消失后的内存占用恢复情况, 是比较直接的判断内存泄漏的依据。
内存占用
如果观察到内存使用情况,在每次Full GC后,占用 的内存都没能恢复到原来的水平,如果在压力撤除 一段时间后,内存依旧不能恢复,那么十有八九当 前系统存在内存泄漏。
HashSet 无序,不可重复
TreeSet 有序,不可重复
Set
• HashSet
哈希散列存储,通过哈希值来决定元素的存储位置,如果位 置上没有对象,则存放;如果存在对象,则判断是否为同一 对象,是则不存放,否则在下方顺延
HashSet是基于HashMap来实现的,操作很简单,更像是对 HashMap做了一次“封装”,而且只使用了HashMap的key来实现 各种特性,而HashMap的value始终都是PRESENT。
List 有序,可重复
• LinkedList
优点: 底层数据结构是链表,查询慢,增删快。? 缺点: 线程不安全,效率高
List 有序,可重复
• 如果列表容量足够(不用扩容),添加元素到尾部,推 荐使用ArrayList,效率高于LinkedList。
• 除了一些极端的情况(首尾),添加元素到列表任意位 置,LinkedList效率高于ArrayList。所以如果有在任意位 置插入元素的需求,可以考虑LinkedList。
• 尽量使用HashMap、ArrayList,除非线程安全需要,否则不推荐 使用Hashtable、Vector,后两者由于使用同步机制而导致了性能 开销(ConcurrentHashMap)
• 基于效率和类型检查的考虑,应该尽可能使用array,无法确定数 组大小时才使用ArrayList
• 需要快速随机插入,应该使用LinkedList,如果需要快速随机访问元 素,应该使用ArrayList
编码技巧
• 尽可能使用局部变量
局部变量存储在栈中,速度较快,栈中创建的变量,随着方法的运行结束,这些内 容就没了,不需要额外的垃圾回收。随用随取,用完就丢。
• 尽量减少对变量的重复计算
明确一个概念,对方法的调用,存在消耗(栈帧,调用方法保护恢复现场)
改为:
编码技巧
• 乘法和除法使用移位操作
用移位操作可以极大地提高性能,因为在计算机底层,对位的操作是最方便、最快的;
写); • 异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Java将IO读写
委托给OS处理,需要将数据缓冲区地址和大小传给OS(银行卡和密码),OS需要支持 异步IO操作API); • 阻塞 : ATM排队取款,你只能等待(使用阻塞IO时,Java调用会一直阻塞到读写完 成才返回); • 非阻塞 : 柜台取款,取个号,然后坐在椅子上做其它事,等号广播会通知你办理, 没到号你就不能去,你可以不断问大堂经理排到了没有,大堂经理如果说还没到你 就不能去(使用非阻塞IO时,如果不能读写Java调用会马上返回,当IO事件分发器 会通知可读写时再继续进行读写,不断循环直到读写完成)
性能瓶颈识别小结
• 一个项目的瓶颈,针对服务器来说,主要在CPU,内存, 网络,磁盘。只有找到瓶颈的问题在哪里,我们才可以 对症下药
代码优化
2Part •代码优化
1. 编码技巧 2. 数据结构的合理选择 3. IO的选择和使用
内存分布
方法区 Method Area
堆 Heap
JVM运行时数据区 Runtime Data Area
Hashtale在写入时会比较慢。
• ConcurrentHashMap
Map
分片存储
Map
• TreeMap
TreeMap基于红黑树(Red-Black tree)实现。该映射根据其键的自然顺序进行 排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方 法
选择技巧
组 • 尽量避免使用二维数组
课程内容
2Part •代码优化
1. 编码技巧 2. 数据结构的合理选择 3. IO的选择和使用
数据结构的合理选择
针对数据结构,主要讲解的是List,Set ,Map
List 有序,可重复
• ArrayList
优点: 底层数据结构是数组,查询快,增删慢。? 缺点: 线程不安全,效率高
注意: 对于ArrayList来说,for循环的效率是最高的, iterator 次之,forEach反而最低。LinkedList使用for 循环完全没法忍受,原因是每次循环都会遍历一次列 表; forEach会比iterator快
Set
Set里存放的对象是无序,不能重复的,集合中的对 象不按特定的方式排序,只是简单地把对象加入集 合中。
虚拟机栈 VM Stack
本地方法栈 Native Method Stack
程序计数器 Program Counter Register
由所有线程共享的数据区
线程隔离的数据区
对象的访问方式
一般来说,一个Java的引用访问涉及到3个内存区域:JVM 栈,堆,方法区。 以最简单的本地变量引用:Object obj = new Object()为例:
• new Object()作为实例对象数据存储在堆中; • 堆中还记录了Object类的类型信息(接口、方法、fiel
d、对象类型等)的地址,这些地址所执行的数据存储在 方法区中; 在Java虚拟机规范中,对于通过reference类型引用访问具体 对象的方式并未做规定,目前主流的实现方式主要有两种:
IO的选择和使用
• BIO: 同步、阻塞
一个连接一个 线程
1. 优点:代码简单、直观 2. 缺点:IO效率、扩展性存在局限性,会成为应用性能的瓶颈
IO的选择和使用
• NIO:同步、非阻塞
一个请求一个线程
1.可以构建多路复用的、同步非阻塞 IO 程序 2.同时提供了更接近操作系统底层的高性能数据操作方式。
网络IO
• 百度文库多时候大家都容易忽略网络对系统的影响,实际上网络带宽在一些情况 下也会成为系统的瓶颈。一旦在业务的请求和响应中包含较大的数据传输 时,往往会遇到网络瓶颈。因为更多的时候服务器采用的还是以太网卡, 1000M网卡在全双工模式下传输速率也只有80M/s,如果响应中包含报表、 图片之类的大尺寸数据,很有可能在性能测试中出现网络瓶颈。
如果指定了一个类为final,则该 类所有的方法都是final的。Java编译器会寻找机会 内联所有的final方法,内联对于提升Java运行效率作用重大。
确保唯一,不然就是子子孙孙无穷尽也
编码技巧
• 尽量重用对象
特别是String对象的使用,出现字符串连接时应该使用StringBuilder/StringBuffer 代替。由于Java虚拟机不仅要花时间生成对象,以后可能还需要花时间对这些对象进行 垃圾回收和处理,因此,生成过多的对象将会给程序的性能带来很大的影响。 (String对象改变一次,新生成一个对象)。集合里创建对象,同理
Key |
value
Map
• HashMap
Hash数组后边跟着小链表/红黑树
链表散列(1.7)
数组+链表+红黑树(1.8)
Map
• Hashtable
与 HashMap类似,不同的是:key和value的值均 不允许为null;它支持线程的同步,即任一时刻 只有一个线程能写Hashtable,因此也导致了
课程内容
性能瓶颈 代码层面
Jvm层面
编
结
io
异
参
回
码
构
流
常
数
收
家里厨房的水池下水堵 了,我们要找原因,首先 得知道水池的下水道都有 哪些部分:
简单的看,可以把下水道 分解为水漏、上连接管、 回水弯、下连接管最后接 入地漏。再查找堵塞位置 时,我们就可以将水直接 导入回水弯,排除水漏和 上连接管道堵塞的可能
选择技巧
• 可以借用set对list进行去重
• 使用最有效率的方式去遍历Map(entry)
代码优化
2Part •代码优化
1. 编码技巧 2. 数据结构的合理选择 3. IO的选择和使用
概念
先来个例子理解一下同步,阻塞的概念,以银行取款为例: • 同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读
编码技巧
• 尽量避免随意使用静态变量
private static B b = new B(); A类不被卸载,那么引.用B指向的B对象会常驻内存,直到程序终止。
• 公用的集合类中不使用的数据一定要及时remove掉(内 存泄漏的隐患)
• 静态类、单例类、工厂类将它们的构造函数置为private • 尽量使用System.arraycopy ()代替通过来循环复制数
HashSet不允许重复(HashMap的key不允许重复,如果出现重 复就覆盖),允许null值,非线程安全
Set
• HashSet内存泄漏
hash集合在操作不当的情况下,有可能造成内存泄漏。 考虑这样的情况,把一个对象存储进hashSet集合后,修改这个对象中参与计算hash的 变量的值,这时这个对象的hash值也会随之改变,那么这么对象可以正常地被删除吗?
• 还有一点就是不要忽略回环地址传输的影响,比如一些应用访问本地监听 的其他服务,都会受到网卡的传输速率限制的影响
磁盘IO
• 通常情况下,磁盘是计算机中速度最慢的一个子系统,因此很多情况中, 磁盘I/O会成为系统的瓶颈。实际上在设计高性能系统的时候,会把避免磁 盘I/O作为一个首要准则。
• 虽然当前的技术发展让存储系统的读写速度不断提升,但高昂的成本使得 大多数情况下,高速存储会使用在数据库或文件服务器上,而不会使用在 应用服务器中。所以在我们进行性能测试时,要更多的注意应用服务器的 磁盘使用情况。
常见性能瓶颈的识别
1 Part
•常见性能瓶颈的识别
1. CPU占有率 2. 内存占用 3. 网络IO 4. 磁盘IO
CPU占有率
• CPU的高占用,并不一定表示有问题,因为实现最优性能的一方面就是充 分发挥当前的硬件资源能力.但是CPU长期处于如图状态,就很值得我们关 注,至少说明在大多数情况下,系统已经是在耗用最大的计算能力进行计 算,运算能力已经成为瓶颈
否
Set
• TreeSet
TreeSet是基于TreeMap实现的。TreeSet中的元素支持2种排序方式:自然排序 或 者 根据创建TreeSet 时提供的 Comparator 进行排序。这取决于使用的构造方法。
TreeSet不支持快速随 机遍历
Map
独立于Collection存在的接口,如下图:(简约图)
对象的访问方式
通过句柄访问的实现方式中,JVM堆中会专门有一块区域用来 作为句柄池,存储相关句柄所执行的实例数据地址(包括在堆 中地址和在方法区中的地址)。这种实现方法由于用句柄表示 地址,因此十分稳定
对象的访问方式
通过直接指针访问的方式中,reference中存储的就是对象在 堆中的实际地址,在堆中存储的对象信息中包含了在方法区中 的相应类型数据。这种方法最大的优势是速度快,在HotSpot 虚拟机中用的就是这种方式
• 尽量在合适的场合使用单例
使用单例可以减轻加载的负担、缩短加载的时间、提高加载的效率,但并不是所有 地方都适用于单例,简单来说,单例主要适用于以下三个方面: (1)控制资源的使用,通过线程同步来控制资源的并发访问 (2)控制实例的产生,以达到节约资源的目的 (3)控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实 现通信
编码技巧
在了解对象访问方式和存储方式之后,我们就需要针对对象的存放和访问来书 写和规范我们的代码。
创建
java
面向对象
使用
减少创建,调高效率,及时销毁
销毁
编码技巧
• 尽量指定类、方法的final修饰符
带有final修饰符的类是不可派生(最后的,最终的)的,不可被继承,final修饰符 可以让方法不可以被重写。
IO的选择和使用
• NIO2/AIO:异步、非阻塞
1. 引入了异步非阻塞 IO 方式 2. 异步 IO 操作基于事件和回调机制—我们不需要像NIO编程那样创建一个独立的I/O
内存占用
• 内存在性能测试中是被重点关注的指标,因为它是反映重大缺陷——内存 泄露的最直接指标,但是我们应该注意到,在JAVA框架中的内存泄漏是发 生在虚拟内存中的。
• 观察内存/虚拟内存的占用情况,尤其是在压力消失后的内存占用恢复情况, 是比较直接的判断内存泄漏的依据。
内存占用
如果观察到内存使用情况,在每次Full GC后,占用 的内存都没能恢复到原来的水平,如果在压力撤除 一段时间后,内存依旧不能恢复,那么十有八九当 前系统存在内存泄漏。
HashSet 无序,不可重复
TreeSet 有序,不可重复
Set
• HashSet
哈希散列存储,通过哈希值来决定元素的存储位置,如果位 置上没有对象,则存放;如果存在对象,则判断是否为同一 对象,是则不存放,否则在下方顺延
HashSet是基于HashMap来实现的,操作很简单,更像是对 HashMap做了一次“封装”,而且只使用了HashMap的key来实现 各种特性,而HashMap的value始终都是PRESENT。
List 有序,可重复
• LinkedList
优点: 底层数据结构是链表,查询慢,增删快。? 缺点: 线程不安全,效率高
List 有序,可重复
• 如果列表容量足够(不用扩容),添加元素到尾部,推 荐使用ArrayList,效率高于LinkedList。
• 除了一些极端的情况(首尾),添加元素到列表任意位 置,LinkedList效率高于ArrayList。所以如果有在任意位 置插入元素的需求,可以考虑LinkedList。
• 尽量使用HashMap、ArrayList,除非线程安全需要,否则不推荐 使用Hashtable、Vector,后两者由于使用同步机制而导致了性能 开销(ConcurrentHashMap)
• 基于效率和类型检查的考虑,应该尽可能使用array,无法确定数 组大小时才使用ArrayList
• 需要快速随机插入,应该使用LinkedList,如果需要快速随机访问元 素,应该使用ArrayList
编码技巧
• 尽可能使用局部变量
局部变量存储在栈中,速度较快,栈中创建的变量,随着方法的运行结束,这些内 容就没了,不需要额外的垃圾回收。随用随取,用完就丢。
• 尽量减少对变量的重复计算
明确一个概念,对方法的调用,存在消耗(栈帧,调用方法保护恢复现场)
改为:
编码技巧
• 乘法和除法使用移位操作
用移位操作可以极大地提高性能,因为在计算机底层,对位的操作是最方便、最快的;
写); • 异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Java将IO读写
委托给OS处理,需要将数据缓冲区地址和大小传给OS(银行卡和密码),OS需要支持 异步IO操作API); • 阻塞 : ATM排队取款,你只能等待(使用阻塞IO时,Java调用会一直阻塞到读写完 成才返回); • 非阻塞 : 柜台取款,取个号,然后坐在椅子上做其它事,等号广播会通知你办理, 没到号你就不能去,你可以不断问大堂经理排到了没有,大堂经理如果说还没到你 就不能去(使用非阻塞IO时,如果不能读写Java调用会马上返回,当IO事件分发器 会通知可读写时再继续进行读写,不断循环直到读写完成)
性能瓶颈识别小结
• 一个项目的瓶颈,针对服务器来说,主要在CPU,内存, 网络,磁盘。只有找到瓶颈的问题在哪里,我们才可以 对症下药
代码优化
2Part •代码优化
1. 编码技巧 2. 数据结构的合理选择 3. IO的选择和使用
内存分布
方法区 Method Area
堆 Heap
JVM运行时数据区 Runtime Data Area
Hashtale在写入时会比较慢。
• ConcurrentHashMap
Map
分片存储
Map
• TreeMap
TreeMap基于红黑树(Red-Black tree)实现。该映射根据其键的自然顺序进行 排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方 法
选择技巧
组 • 尽量避免使用二维数组
课程内容
2Part •代码优化
1. 编码技巧 2. 数据结构的合理选择 3. IO的选择和使用
数据结构的合理选择
针对数据结构,主要讲解的是List,Set ,Map
List 有序,可重复
• ArrayList
优点: 底层数据结构是数组,查询快,增删慢。? 缺点: 线程不安全,效率高
注意: 对于ArrayList来说,for循环的效率是最高的, iterator 次之,forEach反而最低。LinkedList使用for 循环完全没法忍受,原因是每次循环都会遍历一次列 表; forEach会比iterator快
Set
Set里存放的对象是无序,不能重复的,集合中的对 象不按特定的方式排序,只是简单地把对象加入集 合中。
虚拟机栈 VM Stack
本地方法栈 Native Method Stack
程序计数器 Program Counter Register
由所有线程共享的数据区
线程隔离的数据区
对象的访问方式
一般来说,一个Java的引用访问涉及到3个内存区域:JVM 栈,堆,方法区。 以最简单的本地变量引用:Object obj = new Object()为例: