Java 为什么要避免使用finalizer和Cleaner
java9之前finalizer,java9使用cleaner代替了finalizer。相比finalizer,cleaner(它存在于一个独立类cleaner中,需要时候注入到对应类中即可)不会污染api而且cleaner有类库可以控制它的线程(它两都在后台线程中执行)。
避免使用的原因:
行为的不稳定性
它两都不能保证及时的执行,从方法可达到(对象被置空了)开始到最终的执行,时间是任意长的。所以千万不要使用他们来更新重要的持久状态,如释放流资源、分布式锁等。
system.gc和system.runfinalization这两个方法会增加finalizer和cleaner被执行的机会,但是不保证一定会执行。唯一能保证它两会被执行的两个方法(system.runfinalizersonexit和runtime.runfinalizersonexit)有致命的缺陷,已经被废除很久了。
移植性问题
不同的jvm堆垃圾回收的算法不同,如果程序依赖finalizer或者cleaner被执行的时间点,那么程序的表现可能截然不同
性能问题
finalizer和cleaner有一个非常严重的性能损耗。
安全问题
- finalizer中如果出现异常会导致线程终止,但是不会打印线程轨迹甚至警告都不会打印出来,而且使正在销毁的对象处于破坏状态,另一个线程如果使用这个破坏状态的对象会出现行为的不确定性。cleaner没有这个问题。
- finalizer攻击:利于finalizer方法,构建出恶意子类对象,非法调用父类方法。final类不会被构建恶意子类,所以不会遭到finalizer攻击。对于非final类,重写一个空的finalizer方法并用final修饰来防止finalizer攻击。
//构建对象使用后不能再次被实例化 public class demo{ private boolean flag = true; //防止实例化 public demo() { if (flag){ throw new runtimeexception("不准许再次创建对象"); } } public void say() { system.out.println("demoutils.say"); } } //构建非法子类 class demo2 extends demo{ public demo2(){} //构建finalizer攻击 @override protected void finalize() throws throwable { //会调用父类方法 this.say(); system.exit(0); } public static void main(string[] args) throws interruptedexception { try { //创建子类对象必然会调用父类构造,所以会发生异常 //但是在gc中还是执行了父类的方法 demo demo = new demo2(); demo.say(); } catch (exception e) { system.out.println(e); } system.gc(); //给垃圾回收提供时间 thread.sleep(5000); } } //运行结果 java.lang.runtimeexception: 不准许再次创建对象 demoutils.say
两个用处:
安全网
当资源的所有者忘记使用close方法的时候,finalizer和cleaner可以充当安全网,虽然不能保证及时的释放资源,但是迟一点释放总比永远不释放要好。要使用这样的安全网就要认证的考虑清除是否值得付出这样的代价。所以java一些autocloseable实现中都添加了安全网。
这是fileoutputstream的源码
回收本地对等体对象
本地对等体:java操作native方法其实是委托给一个本地对等体对象,使用完成后java对象会被gc回收,但是这个对等体对象不是java对象不会被会gc回收。如果这个对象性能可以接受,而且没有需要及时释放的资源那么就可以使用finalizer或者cleaner进行回收了。但是如果这个对等体性能无法接受且拥有必须被及时终止的资源,那么就需要提供一个close方法了。
以上就是java 为什么要避免使用finalizer和cleaner的详细内容,更多关于java 避免使用finalizer和cleaner的资料请关注其它相关文章!
上一篇: 详解redis集群选举机制
下一篇: Java ArrayList使用总结