第八章--第三节:性能优化的代码调优
第八章:软件构造的性能
第三节:性能优化的代码调优
问题一:代码调优的策略和过程
代码调优:代码调优不是为了修复bug,而是对正确的代码进行修改以提高其性能。
- 调优不会减少代码行数
- 不要猜原因,而应有明确的优化目标
- 不要边写程序边调优
- 不是性能优化的第一选择
- 代码行数与性能之间无必然的联系
- 代码调优建立在对程序性能的精确度量基础之上(profiling)
问题二:代码调优的模式
1.单例模式(Singleton Pattern)
含义:某些类在应用运行期间只需要一个实例。
理解:程序员一般需要对象时就new,导致创建多个object。强制client只能创建一个object实例,避免因为new操作所带来的时空性能(尤其是GC)的损失,也便于复用。
方法:
- 设置静态变量来存储单一实例对象
- 将构造器设置为private,从而client无法new
- 提供静态方法来获取单一实例对象
- 进一步提升性能:在需要的时候再new,而非提前构造出来
2.轻量模式(Flyweight Pattern)
问题特征:考虑文本编辑器中的“字符”,同一个字符重复出现多次,代表同样内容,但字体字号等不同。
含义:该模式允许在应用中不同部分共享使用objects,降低大量objects带来的时空代价。
对象的内部状态:不管在什么场合使用该object,内部特征都不变。
对象的外部状态:不是固定的,需要在不同场合context分别指派/计算其值。
例:游戏中的玩偶,有很多个,每个玩偶具有某些固有属性(Shape),但玩偶在不同的场景下颜色不同(Color)。
无需为每个玩偶new一个object,可以复用。↓↓↓
- 轻量模式和单例模式的对比
单例:不区分各场合下的不同表现形式,统一用一个实例来表示
轻量对象:同一个事物,具有多种不同的表现形式。
(故Flyweight在object层面的复用比Singleton更灵活)
4.原型模式(Prototype Pattern)
含义:通过克隆而非new来创建object
原因:直接new的时空代价高,尤其是需要与外部I/O、网络、数据库打交道时候。
实例:
- Object.clone()是protected:它可以被同包(java.lang)下以及它(java.lang.Object)的子类访问。
- 自定义类无法直接使用Object.clone():没有访问权限(invisible),故需要override。
引用拷贝VS对象拷贝
①浅拷贝:使用一个已知实例的成员变量对新创建实例的成员变量逐个赋值。
(只复制对象本身,但不复制对象对外的引用)
②深拷贝:类的拷贝方法不仅要复制对象的所有非引用成员变量值(简单数据类型),还要为引用类型(对象)的成员变量创建新的实例,并且初始化为原对象的值。
- clone()新对象与原对象要相互独立,要求deep copy
- 缺省实现是shallow copy而非deep copy!
- 在让你的ADT支持Cloneable接口的时候,千万注意自己override的clone()正确的实现了deep copy。
4.对象池模式(Object Pool Pattern)
问题描述:仍然是关于new的巨大性能代价,很多时候,object不用了就直接扔掉,需要时再new一个新object。
含义:对象复用:不要扔掉object,留着后续复用
代价:原本可被GC的对象,现在要留在pool中,导致内存浪费——用空间换时间。
5.规范化(Canonicalizing Objects)
含义:不要保留对象的多个拷贝,仅保留少量object即可。
- ==比equals()的效率更高,所以尽可能将object转化为简单数据类型加以存储和处理
- 用int取代其他常量object
- int比String的时空性能更高
6.规避垃圾回收(Avoiding Garbage Collection)
核心思想:减少创建object的数量,避免GC的代价
例:
- 尽可能使用简单数据类型,对类的成员变量也是如此。
- 局部的简单数据类型在stack中存储,GC代价低;
- 类的简单类型成员变量在GC的时候代价也低,若为object成员变量则需GC
7.对象初始化(Object Initialization)
问题三:对Strings的代码调优
字符串常量池:是堆中的一块特殊的内存区域用来存储字符串对象的引用。
-
,首先检查字符串常量池,若字符串存在在其中,则直接让s指向这个字符串常量;否则在池中创建一个新的字符串对象。
-
都创建在堆中。
**例: