提升Java代码质量(二)
Item5:消除过期对象的引用
JVM为我们实现了GC(垃圾回收)的功能,让我们从手工管理内存中解放了出来,这固然很好,但并不意味着我们就再也不需要去考虑内存管理的事情了;我们用简单的栈实现的例子来解释:
public class Stack {
private Object[] elements;
private in size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack(){
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e){
ensureCapacity();
elements[size++] = e;
}
public Object pop(){
if(size == 0){
return new EmptyStackException();
}
return element[--size];
}
//保证每当栈满就会自动扩容为原来的两倍
private void ensureCapacity(){
if(elements.length == size){
elements = Arrays.copyOf(elements, 2*size+1);
}
}
}
这段程序没有什么明显的错误,无论怎么测试,结果似乎都是正确的,但不严格的讲,这段程序存在"内存泄漏"的风险,极端情况下,会导致磁盘交换(Disk Paging),甚至是程序失败(OutOfMemoryError);
为什么是内存泄漏?书中给出的解释是:从栈中pop出来的对象的引用还被栈内部维护着,这些引用被称作"过期引用"(指下标小于size的那些元素的引用),所以,GC不会去处理该对象以及该对象所引用的其他对象,内存就被这样渐渐"填满"导致溢出.
修复方法,改写pop()
public Object pop(){
if(size == 0){
throw new EmptyStackException();
}
Object result = element[--size];
elements[size] = null; //清空出栈对象的引用,告知GC可以释放其资源
return result;
}
手动将过期引用告知垃圾回收器还有的好处就是,如果之后又用了此过期引用,程序就会报空指针异常,而不是一直"错误下去"..
内存泄漏的另一常见来源是缓存,对象引用在缓存中就很容易被忘记,如果不用它会长时间驻留缓存.对应的几种解决方法:
1.使用WeakHashMap代表缓存,该类拥有expungeStaleEntries方法,用于清除那些外部没有引用并且缓存内存在的键,来段代码说明:
public class Test {
public static void main(String[] args) throws Exception {
String a = new String("a");
String b = new String("b");
Map weakmap = new WeakHashMap();
Map map = new HashMap();
map.put(a, "aaa");
map.put(b, "bbb");
weakmap.put(a, "aaa");
weakmap.put(b, "bbb");
map.remove(a);
a=null;
b=null;
System.gc();
}
}
由于HashMap移除了a的引用,且清除了a的外部引用,此时a的引用只有WeakHashMap来维护了,此时WeakHashMap会自动舍弃掉a,但HashMap还保存着b的引用,所以b不会被WeakHashMap丢弃掉。在工程当中WeakHashMap是很实用的,我们使用短时间内就过期的缓存时最好使用weakHashMap。
2.可以将清除过期引用的任务交给“Time”或“ScheduledThreadPoolExecutor”来完成
即定时清除过期引用。
3.使用LinkedHashMap当做缓存,调用removeEldesttEntry方法清除过期引用
4.直接使用java.lang.ref
说道ref包,就绕不开Java的四类引用:强引用,软引用,弱引用,虚引用。这里大致概括一下,具体引用就不过多赘述,细节可以参照深入探讨java.lang.ref包
强引用:只要引用存在,垃圾回收器永远不会回收,Object obj = new Object()的形式
软引用:非必须引用,内存溢出之前进行回收,可以通过以下代码实现,SoftReference<Object> sf = new SoftReference<Object>(obj)的形式,通过sf.get()获取对象,软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据。
弱引用:第二次垃圾回收时回收,WeakReference<Object> wf = new WeakReference<Object>(obj)的形式,弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器标记。
虚引用:垃圾回收时回收,无法通过引用取到对象值,PhantomReference<Object> pf = new PhantomReference<Object>(obj)的形式,pf.isEnQueued()主要用于检测对象是否已经从内存中删除。
关于内存泄漏的检测,往往不会表现的很明显,但它能在系统中存在很多年,只有通过检查代码或借助于Heap剖析工具(Heap Profiler)发现问题,关于Heap Profiler我有机会在单独介绍。
Item6:避免使用终结方法(finalizer)
下面由一段代码引入:
/**
* @author YHW
* @ClassName: Test1
* @Description:
* @date 2019/1/4 20:55
*/
public class Test1 { public static void main(String[] args){
Bob bob = new Bob();
System.out.println(bob.getState());
Thread thread = new Thread(){
@Override
public void run() {
super.run();
try{
sleep(2000);
}catch(Exception e){
e.printStackTrace();
}finally{
bob.setClosed(true);
} }
};
thread.start();
System.out.println(bob.getState());
} }
运行结果截图:

在main方法中开了一个子线程,我故意睡了2秒,故根本走不到终结方法那里,主程序就”关闭“了,这时候子线程还没执行完,这有可能因此而产生终结对象的速度达不到对象入队的速度,出现内存泄漏而死掉,所以我们不应该以来终结方法来更新重要的持久状态,而且并没有很轻便的方法能够避免这样的问题;
由此引出了终结方法所具备的特性:
1.finalizer方法的线程优先级比当前程序的其他线程优先级要低,且JAVA语言规范不保证任何线程中finalizer方法的执行;
2.及时执行终结方法是JVM的一大功能,但在不同的JVM实现都大相径庭,有时候终结方法的线程优先级会非常低,造成JVM“没时间”释放不用的资源,引起OutOfMemoryError;
3.唯一声称保证终结方法执行的System.runFinalizersOnExit和Runtime.runFinalizersOnExit都存在致命缺陷,已经废弃了;
4.finalizer会有非常严重的性能损失;
提升Java代码质量(二)的更多相关文章
- 提升Java代码质量(三)
Item7:覆盖equals时需要遵守通用约定 在我们日常开发过程中,重写equals是比较常用的,但存在许多不合适的覆盖方式导致错误,最好的避免方法就是不去重写equals.但有时我们的业务又需要建 ...
- 提升Java代码质量(一)
博主双12入手了一本"Effective Java第二版",本系列文章将初步梳理书中内容,我也查了些资料,我会针对知识点做一点展开,方便以后复习回顾; Item1.考虑用静态工厂代 ...
- 拔高你的Java代码质量吧:推荐使用枚举定义常量(转)
提高你的Java代码质量吧:推荐使用枚举定义常量 一.分析 常量的声明是每一个项目中不可或缺的,在Java1.5之前,我们只有两种方式的声明:类常量和接口常量.不过,在1.5版之后有了改进,即新增了一 ...
- 提高Java代码质量的Eclipse插件之Checkstyle的使用详解
提高Java代码质量的Eclipse插件之Checkstyle的使用详解 CheckStyle是SourceForge下的一个项目,提供了一个帮助JAVA开发人员遵守某些编码规范的工具.它能够自动化代 ...
- java代码解析二维码
java代码解析二维码一般步骤 本文采用的是google的zxing技术进行解析二维码技术,解析二维码的一般步骤如下: 一.下载zxing-core的jar包: 二.创建一个BufferedImage ...
- 提高Java代码质量的Eclipse插件之Checkstyle的使用具体解释
CheckStyle是SourceForge下的一个项目,提供了一个帮助JAVA开发者遵守某些编码规范的工具.它可以自己主动化代码规范检查过程.从而使得开发者从这项重要可是枯燥的任务中解脱出来. Ch ...
- java 性能优化:35 个小细节,让你提升 java 代码的运行效率
前言 代码 优化 ,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没 ...
- JAVA性能优化:35个小细节让你提升java代码的运行效率
代码优化,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没用,但是, ...
- Java代码质量度量工具大阅兵
FindBugs FindBugs, a program which uses static analysis to look for bugs in Java code. It is free so ...
随机推荐
- codevs 1576最长严格上升子序列
传送门 1576 最长严格上升子序列 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 黄金 Gold 题目描述 Description 给一个数组a1, a2 ... an ...
- Oracle查询24小时制的时间格式
select to_date('2016-11-11 13:42:27', 'yyyy/mm/dd hh24:mi:ss') from dual.
- Spring boot 2.0 学习
Spring boot 2.0出来了,支持java 9, 好多新特性,应该学习. 待续... ...
- Javascript Tip(!!)
var a:var b=!!a; a默认是undefined.!a是true,!!a则是false,所以b的值是false,而不再是undefined,也非其它值,主要是为后续判断提供便利. !!一般 ...
- thinkpad取消fn键功能
转自:https://bbs.thinkpad.com/thread-1834235-1-1.html 1就是一直觉得fn建自动开启很烦人,于是百度后得到 我们可以 控制面板 -- 键盘--think ...
- [hiho第92周]Miller-Rabin素性测试的c++实现
证明: 如果n是素数,整数$a$ 与$n$ 互素,即$n$ 不整除$a$ ,则${a^{n - 1}} \equiv 1(\bmod n)$ ,如果能找到一个与$n$ 互素的整数$a$ ,是的上式不成 ...
- matlab下的caffe接口配置(Windows)
本文基于大部分网上方法 http://blog.csdn.net/d5224/article/details/51916178,外加一点自己的个人实际配置经历,环境变量在配置后尽管显示正确并且重启多次 ...
- plsql 分页
select * from (select rownum rn,ps.* from (select * from user_t) ps ) where rn>=1 and rn<=10 ...
- Umbraco image中使用Crop URL
需要在Umbraco 的image中使用crop URL.首先你需要取出这个image作为IPublishedContent 有以下两种方法 第一种: var imageId = Model.Cont ...
- LeetCode: 492 Construct the Rectangle(easy)
题目: or a web developer, it is very important to know how to design a web page's size. So, given a sp ...