转自http://www.threaddeath.com/

闲逛ITEye时看到了译帝的一篇翻译博客,其中提到了关于Java类重写finalize方法后带来的诡异的GC overhead limit问题。博客的结尾非常详细的说明了这个问题产生的原理,但是始终有一个地方没有得到清晰的答案:由于finalize方法是Object类的protected方法,即无论重写与否,所有的Java类都会带有finalize方法,但为什么只有重写之后才会出现GC问题,不重写与重写的真实差别到底在哪儿?

通过思考始终得不到答案,索性打开Eclipse直接调试代码:

首先验证了博客中的现象的确可以重现。

中间一度怀疑问题的原因可能是finalize方法中引用了类的静态变量AtomicInteger会引起GC的问题(实际上这个方向是错误的,GC是根据对象是否存在被引用关系来判断对象是否回收,惭愧),删掉原来的方法体然后随意在finalize方法中声明了一个变量,结果运行时问题仍然存在。

到了此时,开始怀疑是不是和finalize方法体有关系了,直接删除掉finalize方法体中的所有内容,运行后发现问题真的不存在了!

由于出现GC问题时内存堆中存在大量Finalizer对象,而Finalizer对象只能通过Finalizer类的静态register方法创建,因此尝试在register方法中加上断点对两种情况分别运行,发现方法体为空时,register方法不会被调用,这样自定义对象便没有被任何对象引用,可以轻松的被GC回收掉。

由于register方法是被VM所调用,只能求助于莫枢大神,而莫枢也确认了只要Java类以及它所有的祖先类中不含有finalize方法或者finalize方法体为空时,VM便不会将该类的对象实例注册为finalizable对象(地址)。

至此,算是把整个问题的来龙去脉给整理的差不多了:

JVM创建自定义对象。

JVM检测对象类以及祖先类是否含有非空的finalize方法定义,如果均没有,则不进行后续的Finalizer相关处理。

JVM调用Finalizer类的静态register方法,创建包含该对象引用的Finalizer对象,同时Finalizer静态类将该Finalizer对象加入到一个单向链表中。

运行过程中,JVM会将这些Finalizer对象的状态更新为pending(这部分可以参考java.lang.ref.Reference类中的说明)。

Reference类中的ReferenceHandler线程会扫描到这些状态为pending的Finalizer对象,将这些对象enqueue到Finalizer静态类引用的ReferenceQueue(非Finalizer中的单向链表)当中。

Finalizer线程从ReferenceQueue中逐一弹出Finalizer对象,首先将Finalizer对象从Finalizer的单向链表中删除,解除了Finalizer静态类对Finalizer对象的引用关系,之后调用Finalizer对象引用的自定义对象的finalize方法,Finalizer对象以及自定义对象此时均可被GC回收。

由于Finalizer线程的低优先级,可能引起旧对象的释放速度无法跟上新对象的创建速度,引起OutOfMemory问题(例子中的GC overhead limit原因是由于新对象创建的代价太低儿就对象回收的代价较高导致CPU用于GC回收的时间比例超过98%)

最后,网易研究院的马进在他的博客中非常详细的阐述了finalize的原理以及因此引发的案例,也非常值得一读。

java gc与finalize_finalize方法与Java GC相关推荐

  1. java 调用 finalize_java finalize方法总结、GC执行finalize的过程

    注:本文的目的并不是鼓励使用finalize方法,而是大致理清其作用.问题以及GC执行finalize的过程. 1. finalize的作用 finalize()是Object的protected方法 ...

  2. 查询当前电脑安装的Java/JDK版本的方法(查看Java/JRE版本号)

    查看当前电脑安装的Java/JDK版本的方法 1.电脑键盘同时按住Windows+R按键,弹出运行程序,输入cmd并确定 2.在黑框控制台中输入java --version,即可查看Java版本号 3 ...

  3. java十进制小数转二进制方法(Java)StringBuilder

    十进制小数转二进制方法(Java) 文章目录 十进制小数转二进制方法(Java) 重点: 1.整数部分的十进制转二进制 2.小数部分的十进制转二进制 3.可变的字符串类String Builder的使 ...

  4. JAVA线程两种方法内存,JAVA线程基础回顾及内存模型(一)

    线程创建的两种方式 继承Thread类 class MyThread extends Thread{ ...... @Override public void run(){ ...... } } My ...

  5. java不抛出方法异常,java – 重写方法不会抛出异常

    编译我的代码时遇到问题,我试图让一个类的方法抛出一个个性化的异常,给定一些条件.但在编译时我得到的信息是: Overridden method does not throw exception 这是类 ...

  6. Java中的main()方法详解

    源文作者:leizhimin    源文链接:http://lavasoft.blog.51cto.com/62575/53263 源文作者版权申明: 版权声明:原创作品,允许转载,转载时请务必以超链 ...

  7. java中日期比较方法_在java中进行日期时间比较的4种方法

    1. Date.compareTo() java.util.Date 提供了在 Java 中比较两个日期的经典方法compareTo(). 如果两个日期相等,则返回值为0. 如果Date在date参数 ...

  8. Java.io.File.getPath()方法实例

    转载自  Java.io.File.getPath()方法实例 java.io.File.getPath() 方法将抽象路径名到路径名字符串.为了分离名称的序列所产生的字符串使用默认名称分隔符. 声明 ...

  9. java ee 的使用方法_改善Java EE生产支持技能的8种方法

    java ee 的使用方法 参与Java EE生产支持的每个人都知道这项工作可能很困难. 7/24寻呼机支持,多个事件和错误修复(要定期处理),来自客户和管理团队的压力,要求它们尽快解决生产问题并防止 ...

最新文章

  1. Django REST framework的各种技巧——1.基础讲解
  2. Installation of Apache HTTPD
  3. python 内置模块random_Python3.5内置模块之random模块用法实例分析
  4. 数列分块入门4(区间求和)
  5. Java基础93 JDBC连接MySQL数据库
  6. ea建模 教学_周末特惠:EA促销开启,吉你太美首次打折 + EA旗下多款游戏登陆Steam,EA access即将推出...
  7. 教育中的“产出/产能平衡”原则
  8. HTML清除计时器的语句,如何知道计时器是否被清除或在javascript中超时?
  9. android动画实现单摆效果
  10. word参考文献编号、引用、修改
  11. 财务数字转换--大小写转换
  12. LTE/LTE advanced——UMTS 长期演进理论与实践——控制面(3)
  13. 最强大脑记忆曲线(7)——“复习页面”逻辑实现
  14. win7系统怎么用笔记本做wifi热点(转)
  15. vs2012中将图片放到resource中进行调用
  16. 火狐浏览器安全连接失败解决办法
  17. 在线协作编辑器EtherPad使用说明
  18. jpg格式图片怎么转换成pdf格式?如何把jpg图片转换成pdf格式文档?
  19. Unity-点击屏幕进行移动
  20. FabGennix 亲和纯化CD163抗体

热门文章

  1. systemctl命令
  2. 视频分辨率和音频质量_高分辨率音频的声音是否比CD质量好?
  3. devops f5_2017年排名前5的DevOps文章
  4. 开源压缩算法brotli_Brotli:一种新的压缩算法,可加快互联网速度
  5. devops实践指南_开发DevOps的实用指南:减少八卦的步骤
  6. 六课时 一课时45分钟表格_今年的“课时”目标是1亿学生
  7. JS_异步任务之流程控制
  8. Select2的使用 联动筛选框的一个例子
  9. jsp 内置对象 登录 cookie + session
  10. Bootstrap 导入插件