博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[转载]Java 技术中心(Java Technology Centre)的珍品: 与垃圾收集器(Garbage Colle
阅读量:2447 次
发布时间:2019-05-10

本文共 2509 字,大约阅读时间需要 8 分钟。

Java 技术中心(Java Technology Centre)的珍品: 与垃圾收集器(Garbage Collector)为伴

垃圾收集器(Garbage Collector(GC))是一直伴随着 Java 程序员的最有争议的问题之一。我们接受了独立的收集器的原则,但是控制该收集器的迫切需要常常被证明是不可抗拒的。典型情况下,您用好了一个或多个资源, 并指望它们会被回收。但它们并没有被回收。一定出现了问题!IBM 为我们提供了一种强行执行 GC 的方法 ― 调用 System.gc() ,那么使用这一调用就一定没有问题,是这样吗? 错。几乎总是错的。

GC 称为“内存管理器”会更好,因为这是它真正做的事情。同 C/C++ 一样,它根据应用程序的请求分配内存。但是与 C/C++ 不同的是,内存释放是 GC 单独执行的。因为内存管理的复杂性中 99% 与未使用内存的自动定位及释放有关。一般我们提到“GC”就是指 Java 内存管理。

Java 虚拟机(Java virtual machine(JVM))有它自己的内存池。我们不使用一般的本地方法,如 malloc 。当然,这个内存池只是从本机 OS 中分配的一大块。Java 内存被称作 。当 Java 对象需要内存时,就从堆中进行内存分配。

通常,JVM 在被要求时才对内存进行分配。当且仅当内存分配发生错误(内存溢出)时才执行 GC。

是 的,是的,我知道 Java 1.1.8 有“异步 GC”操作。在 IBM Java SDK 中,这个循环实际上从未被激活。因为性能的缘故,异步 GC 操作被配置成只有在您的应用程序确实什么事也不做的时候才会运行。即使您的应用程序静止,也要经过相当长时间的超时,异步 GC 才会运行。在 Java 2 中已经没有异步操作了。

因此,对于 IBM Java SDK 来说,GC 完全是一个同步操作,而且仅在内存分配发生错误时才发生。

我们当然不会完整地讨论这些内容;关于这一主题已经有整本专著问世了。我们只要讨论基础知识。

GC 通过扫描 JVM 里运行的所有线程的堆栈和寄存器来执行。如果它发现有些东西似乎是对 Java 堆内的引用,GC 将会继续查下去。如果该引用确实是对对象的引用,那么 GC 将跟随该对象内的所有引用。被引用的对象以及该对象所引用的所有对象都将被“标记”(或标志)。

显然,只有在线程被中止的情况下它才能起作用,因此,GC 要停止 JVM 里的 所有线程才能开始执行,当然,其中不包括它正在其上运行的那个线程。

标记阶段结束时,GC 会扫描堆,并对照在标记阶段标志的那些对象检查堆里的所有对象。任何没有被标记的对象都是没有被任何线程引用的对象,因而,这些对象将会被回收并被放回到空闲池。

此处需要指出的重点是:

  • GC 是一个停止一切的操作。
  • GC 是一个同步操作。
  • GC 需要扫描每个线程的堆栈。
  • GC 需要扫描整个 Java 堆。

就 CPU 使用和时间而言,GC 操作代价高昂。这就是我们要尽可能减少它的执行的原因。虽然它效率极高,但在一个有许多线程并且堆的大小达多兆字节的环境中运行需要时间。

这解释了相当常见的说法 -“我的对象在应该被回收的时候并没有被回收”。它们符合被回收的条件;可是没有必要运行 GC,所以它并没有执行。

System.gc() 是“内存分配错误是运行 GC 的唯一原因”这一规则的一个例外。

虽然这可能是一种例外情况,但并不是一个好主意。请相信我。

文档说明该调用设置了一个标志,该标志表明在 JVM 非常想要时可以运行 GC。 System.gc() 调用实际上做的事情是:如果调用它的时候有一个 GC 循环正在运行,那么就怱略这次调用;否则,就开始一次完整的 GC 循环。

这就是说,每次(或 99.9% 次)调用 System.gc() 的时候,您都会开始一个完整的 GC 循环。也许根本没有必要运行 GC,但是您还是强行让它执行完整个标记/扫描过程。您完全可以把 System.gc() 看作是 System.StopEverythingForAWhile() 调用或 System.SlugMyPerfomance() 调用。

对 于这个调用,有一个可能的正当理由。因为如果没有这个调用,直到堆用完 GC 才会运行。如果您有一个千兆字节的堆,在 GC 清理全部垃圾时,您将遭到相当大的打击,然后您可能会考虑以固定间隔强行执行一个 GC 循环以达到“少量多次”的效果。但是,在这种情况下,您考虑调整堆的大小会更好些。

到处散布 System.gc() 调用的诱惑非常大。在某一阶段,我们总是遇到“内存溢出”的问题,第一反应是认为 GC 出了问题,并强行执行一个循环。尽管这常常能改善局面(至少是暂时能),并且似乎验证了“GC 出了问题”这一想法,但事实也许并非如此。

请 记住,GC 处于食物链的底部。由于 GC 的常规操作是彻底检查系统运行时的状态,所以代码中其它地方所犯的错误常常会被 GC 暴露出来。这样,别处所犯的错误导致 GC 没有响应,而程序员认为 JVM 是在 GC 处异常结束的。因此,应该是 GC 的问题。追查要从定位问题开始,因此,我们强行让 GC 在代码中的不同地方执行。不幸的是,一旦了解了 GC 查出的问题的真相,我们总是会忘记删除所有那些 System.gc() 调用。

在一天结束时,最终在您的代码 中会有成十、成百或者上千个强行执行 GC 的调用(您有多少个呢?)。除了缓慢的性能和在强制执行的非必要 GC 循环上花费过多的时间之外,这使您一无所获。当允许 GC 按您预想的那样运行时,您可能会感到吃惊,一个劣等系统的响应速度也如此之快。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/374079/viewspace-130110/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/374079/viewspace-130110/

你可能感兴趣的文章
koa express_Koa简介-Express的未来
查看>>
github请求超时_在GitHub中创建第一个请求请求
查看>>
构建自定义JavaScript Scrollspy导航
查看>>
laravel/dusk_Laravel Dusk简介
查看>>
slim3框架 教程_SLIM 3入门,PHP微框架
查看>>
谷歌中阻止冒泡在火狐中失效_如何在Google表格中转换货币
查看>>
wps表格日期计算天数_如何计算Google表格中两个日期之间的天数
查看>>
如何使Linux控制台更易于Linux新手使用
查看>>
谷歌浏览器开发文档获取书签_使用书签更快地浏览Word文档
查看>>
如何在Apple Watch上启用和使用缩放
查看>>
初学者:如何在Outlook 2010中向其他人分配任务
查看>>
windows 10 修复_如何修复Windows的10大烦恼
查看>>
系统映像恢复 进不了系统_如何从Windows系统映像中恢复特定文件
查看>>
如何学习Photoshop的极客指南,第2部分:面板
查看>>
如何在iPhone上将实时照片转换为视频或GIF
查看>>
xbmc_从iPhone或iPod Touch控制XBMC
查看>>
编程爱好者网站_读书爱好者的最佳免费网站
查看>>
为什么运行微波会杀死Wi-Fi连接?
查看>>
matlab生成文本乱码_如何生成完全随机/乱码文本的段落
查看>>
siri捷径调用谷歌翻译_告诉它使用Google助理,以减少Siri的麻烦
查看>>