CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛
CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛
CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛

java中final,finally,finalize的区别(太疯狂了)finalized用法,「Java」finalize方法的作用与风险有哪些?,

1.java中final finally finalize的区别

呈现当垃圾收集器(garbage collector 即GC)断定一个对象是死对象(即该对象不可达)时,如果这个对象重写了Object中的finalize方法,GC会调用它的finalize方法调用完之后并不会直接回收,而是当下次进行垃圾收集时再对这个对象进行判断是否可达,如果这时这个对象任然不可达才会回收这个对象,释放这个对象占用的存储空间。

2.java中finalize的作用

也就是如果一个对象重写了finalize方法,这个对象的回收会多一个过程我们看下面这个例子A类重写了finalize方法,如下:publicclassA{ @Overrideprotected

3.final.finally和finalize有什么区别

voidfinalize()throws Throwable { System.out.println(“finalize…”); } }B类没有重写fialize方法,但是包含了一个长度为1024*1024的int类型的数组,即每创建一个B类的对象就会额外创建一个1M大小的int数组,目的就是为了增加创建B对象时这个对象所占用的存储空间,尽快触发垃圾回收动作。

4.finalize与finally的区别

publicclassB {//1M存储空间privateint[] ints = newint[1024 * 1024]; }这个Client类在main方法中创建了一个A类的对象之后就在一个没有终止的for循环之中不停的创建B类的对象。

5.final finally finalized的区别

publicclassClient {publicstaticvoidmain(String[] args){ new A(); for (; ; ) {

6.java final finally finalize的区别

new B(); } } }当我们运行Client类时因为B类的对象占用了大量的堆内存空间,很快就会触发垃圾收集又因为我们只是在创建A类的对象和B类的对象,并没有引用它们,GC的时候会把这些没有引用的对象全部当做垃圾进行回收。

7.请描述final、finally 和 finalize的区别

因为A类重写了finalize方法,所以我们看到控制台打印了如下信息:

8.java中的finalize方法

finalize执行打印的信息说明A对象的finalize方法被执行了finalize方法会在对象变得不可达时被虚拟机(准确地说是虚拟机中的垃圾收集器)执行,且这个方法只会被虚拟机执行一次,但是可以被手动执行任意次,且手动执行后不影响虚拟机对它的执行。

9.简述final、finally和finalize的区别

执行线程那么A对象的finalize方法是由哪个线程执行的呢?我们怀着好奇心把A类的finalize方法重写如下:publicclassA{ @Overrideprotectedvoidfinalize

10.final,finally,finalize的作用和区别

()throws Throwable { //打印当前的执行线程 System.out.println(“finalize Thread.currentThread() = “

+ Thread.currentThread()); } }在Client类中也打印当前线程,代码如下:publicclassClient { public

staticvoidmain(String[] args) { //打印当前线程 System.out.println(“main Thread.currentThread() = “

+ Thread.currentThread()); new A(); for (; ; ) { new

B(); } } }在运行之前我们先对Thread类的toString方法进行一下了解,下图就是Thread类重写的toString方法,重写后的toString方法会打印这个线程的名称、优先级,如果所属的线程组不为空则还会打印所属的线程组的名称:

Thread类的toString方法现在再次运行Client类,结果如下:

执行线程从该图的执行结果中我们可以看到执行A对象finalize方法的线程名称是Finalizer,这个线程的优先级是8,线程所属的线程组是system;执行main方法的线程的名称是main,优先级是5,线程所属的线程组是main。

说明finalize方法是由单独的线程异步执行的,而且这个线程的优先级比普通的执行线程的优先级要高(网上有人说执行finalize的线程的优先级较低,但是我们这里看到的结果却相反)[线程的优先级] 线程的优先级是用1到10的数字表示的,其中1表示最小,10表示最大。

finalize的运行机制需要额外的开销来完成,比如当第一次发现一个对象不可达时且这个对象重写了finalize方法,就会把它放到一个队列当中,后续会有一个后台进程调用这个对象的finalize方法最后在下一次的GC的时候再把它回收(如果此时任然不可达的话)。

根据其运行机制我们可以想象出使用finalize的一些风险,下面我们进行验证风险一:对象复活导致OOM在finalize方法中,可以把其他引用重新指向自己从而达到让自己复活的目的当一个对象的finalize方法执行时,说明这个对象已经是一个死对象了,也就是说此时这个对象已经不可达了。

但是当这个对象在finalize方法中把自己又赋值给了其他引用,导致这个对象又可以通过其他引用可达时,那么这个对象就又复活了下面是一个C类,这个类重写了finalize方法,但是在finalize方法里面会把自己添加到一个静态变量LIST里面,这样自己就可以这个静态变量可达,添加之后当下次GC执行垃圾回收时这个对象就不会被回收。

publicclassC{ //静态字段,用于搜集C类的对象static List LIST = new ArrayList(); privateint[] ints =

newint[1024 * 1024]; @Overrideprotectedvoidfinalize()throws Throwable { //调用父类的finalize方法

super.finalize(); //将当前对象添加进LIST中,使当前对象复活 LIST.add(this); } }现在我们在Client类中不停的创建C对象,如下:

publicclassClient {publicstaticvoidmain(String[] args){ for (; ; ) { new C(); } } }

执行Client类,运行结果如下图:

OOM从上图的结果中可以看出程序很快就抛出了OutOfMemoryError异常,此时堆内存溢出了如果我们在C类中不添加LIST.add(this);,那么Client可以一直运行下去,因为堆内存满了之后GC会进行垃圾收集,但是因为我们添加了LIST.add(this);这行代码导致每个C类的对象都复活了,堆内存释放不了,最后就溢出了。

风险二:占用存储空间导致OOM当垃圾收集器第一次发现一个实现了finalize方法的对象不可达时,会把它存放在一个队列中后续再进行处理这个过程就多出了一个存储的过程,导致这种对象的垃圾收集周期变长,垃圾搜集器对堆内存的清理速度跟不上创建对象所需堆内存的速度,导致OOM。

首先我们先确认一下堆内存的最大值,如下图,我的机器的最大值是1G:

堆大小1024m我们新建两个类D和E,它们两个都含有一个int类型的长度是1024*1024*100(100M)的数组,其中D类重写了finalize方法,E类没有重写,我们在Client类中分别创建一千个D类的对象和E类的对象,然后查看是否会出现OOM。

代码如下:D类重写了finalize:publicclassD{ privateint[] ints = newint[1024 * 1024 * 100]; @Override

protectedvoidfinalize()throws Throwable { super.finalize(); } }E类没有重写finalize:publicclass

E {privateint[] ints = newint[1024 * 1024 * 100]; }Client类创建一千个D类的对象,运行之后很快就出现堆内存溢出,如下:publicclassClient

{ publicstaticvoidmain(String[] args) { long start = System.currentTimeMillis();

for (int i = 0; i < 1000; i++) { new D(); } System.out.println(“耗时:” + (System.currentTimeMillis() – start)); } }

OOM而创建一千个E类的对象时却成功完成:publicclassClient { publicstaticvoidmain(String[] args) { long start = System.currentTimeMillis();

for (int i = 0; i < 1000; i++) { new E(); } System.out.println(“耗时:” + (System.currentTimeMillis() – start)); } }

成功风险三:性能损耗由于垃圾回收的周期变长会导致需要耗费更多的存储资源和线程资源对这种对象进行回收,对性能会有一定的影响我们改造一下类D和E,它们两个都含有一个int类型的长度是1024*1024(1M)的数组,其中D类重写了finalize方法,E类没有重写,我们在Client类中分别创建一万个D类的对象和一万个E类的对象,然后查看出其耗时。

代码如下:D类:publicclassD{ privateint[] ints = newint[1024 * 1024]; @Overrideprotectedvoidfinalize

()throws Throwable { super.finalize(); } }E类:publicclassE {privateint[] ints = newint[1024

* 1024]; }Client类:publicclassClient { publicstaticvoidmain(String[] args) { long start01 = System.currentTimeMillis();

for (int i = 0; i < 10000; i++) { //重写了finalizenew D(); } long start02 = System.currentTimeMillis();

for (int i = 0; i < 10000; i++) { //没有重写finalizenew E(); } long start03 = System.currentTimeMillis();

//重写了finalize方法的耗时long t1 = start02 – start01; //没有重写finalize方法的耗时long t2 = start03 – start02; System.

out.println(“t1 = ” + t1); System.out.println(“t2 = ” + t2); System.out.println(“倍数 = “

+ ((double) t1) / ((double) t2)); } }运行Client类后,执行结果如下:

耗时从执行结果中看出重写了finalize后的耗时大约是没有重写的3.66倍,重写finalize后的对象对程序性能的影响是很高的用途finalize设计的初衷是为了在一个对象被回收之前可以通过finalize方法释放一些系统资源或者做一些清理的工作。

使用finalize的一个好处是当一个对象持有一项资源,并且忘记关闭时,finalize可以作为一个安全网将其关闭,即finalize方法是一个兜底的方案来关闭资源,当这个对象不可达时,finalize方法会被垃圾收集器调用,此时就会把资源关闭。

虽然这并不能保证资源会被及时关闭,这对于不能关闭资源来说,晚一点关闭显然是相对好的处理方式在JDK中就有这样一些充当安全网的finalize方法,比如java.io.FileInputStream类中的finalize方法,这个方法主要是确保当没有引用指向这个输入流对象时,这个对象的close方法能够得到执行:

java.io.FileInputStream类中的finalize比如java.io.FileOutputStream类中的finalize方法,这个方法主要是确保当没有引用指向这个输出流对象时,这个对象的flush方法或者close方法也能够得到执行:

java.io.FileOutputStream类中的finalize比如java.util.concurrent.ThreadPoolExecutor类中的finalize方法主要是确保当没有引用指向这个线程池且这个线程池中没有线程时会调用其shutdown方法:

java.util.concurrent.ThreadPoolExecutor类中的finalize总结finalize方法的执行依赖于虚拟机的实现,finalize方法执行的及时性不能得到保证,在不同的操作系统中执行的时机可能会不一样。

而且java语言规范也不保证它肯定会被执行如下,我们在Client的main方法中只创建一个D类的对象,当这个main方法执行完之后(也就是程序退出之后)创建的D类的对象的finalize方法并没有执行(因为finalize的执行需要垃圾收集器调用,在没有触发垃圾收集时finalize方法不会执行)。

publicclassD{ privateint[] ints = newint[1024 * 1024]; @Overrideprotectedvoidfinalize()throws

Throwable { super.finalize(); } }publicclassClient {publicstaticvoidmain(String[] args)

{ new D(); } }

没有执行finalizefinalize的实现机制需要有一个队列和单独的线程去完成,也就是说会使用更多的计算资源和存储空间,这就决定了finalize方法的执行会损耗性能,也提高了OOM的风险。

© 版权声明
THE END
喜欢就支持一下吧
点赞0赞赏 分享
相关推荐
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容