当前位置: 首页 > news >正文

承德建设网站渭南自建站网站建设

承德建设网站,渭南自建站网站建设,济南市城市建设集团网站,东莞网站制作南城本章概要 垃圾回收器 finalize() 的用途你必须实施清理终结条件垃圾回收器如何工作 垃圾回收器 程序员都了解初始化的重要性#xff0c;但通常会忽略清理的重要性。毕竟#xff0c;谁会去清理一个 int 呢#xff1f;但是使用完一个对象就不管它并非总是安全的。Java 中有…本章概要 垃圾回收器 finalize() 的用途你必须实施清理终结条件垃圾回收器如何工作 垃圾回收器 程序员都了解初始化的重要性但通常会忽略清理的重要性。毕竟谁会去清理一个 int 呢但是使用完一个对象就不管它并非总是安全的。Java 中有垃圾回收器回收无用对象占用的内存。但现在考虑一种特殊情况你创建的对象不是通过 new 来分配内存的而垃圾回收器只知道如何释放用 new 创建的对象的内存所以它不知道如何回收不是 new 分配的内存。为了处理这种情况Java 允许在类中定义一个名为 finalize() 的方法。 它的工作原理假定是这样的当垃圾回收器准备回收对象的内存时首先会调用其 finalize() 方法并在下一轮的垃圾回收动作发生时才会真正回收对象占用的内存。所以如果你打算使用 finalize() 就能在垃圾回收时做一些重要的清理工作。finalize() 是一个潜在的编程陷阱因为一些程序员尤其是 C 程序员会一开始把它误认为是 C 中的析构函数C 在销毁对象时会调用这个函数。所以有必要明确区分一下在 C 中对象总是被销毁的在一个 bug-free 的程序中而在 Java 中对象并非总是被垃圾回收或者换句话说 对象可能不被垃圾回收。垃圾回收不等同于析构。 这意味着在你不再需要某个对象之前如果必须执行某些动作你得自己去做。Java 没有析构器或类似的概念所以你必须得自己创建一个普通的方法完成这项清理工作。例如对象在创建的过程中会将自己绘制到屏幕上。如果不是明确地从屏幕上将其擦除它可能永远得不到清理。如果在 finalize() 方法中加入某种擦除功能那么当垃圾回收发生时finalize() 方法被调用不保证一定会发生图像就会被擦除要是垃圾回收没有发生图像则仍会保留下来。 也许你会发现只要程序没有濒临内存用完的那一刻对象占用的空间就总也得不到释放。如果程序执行结束而垃圾回收器一直没有释放你创建的任何对象的内存则当程序退出时那些资源会全部交还给操作系统。这个策略是恰当的因为垃圾回收本身也有开销要是不使用它那就不用支付这部分开销了。 finalize() 的用途 如果你不能将 finalize() 作为通用的清理方法那么这个方法有什么用呢 这引入了要记住的第3点 垃圾回收只与内存有关。 也就是说使用垃圾回收的唯一原因就是为了回收程序不再使用的内存。所以对于与垃圾回收有关的任何行为来说尤其是 finalize() 方法它们也必须同内存及其回收有关。 但这是否意味着如果对象中包括其他对象finalize() 方法就应该明确释放那些对象呢不是无论对象是如何创建的垃圾回收器都会负责释放对象所占用的所有内存。这就将对 finalize() 的需求限制到一种特殊情况即通过某种创建对象方式之外的方式为对象分配了存储空间。不过你可能会想Java 中万物皆对象这种情况怎么可能发生 看起来之所以有 finalize() 方法是因为在分配内存时可能采用了类似 C 语言中的做法而非 Java 中的通常做法。这种情况主要发生在使用本地方法的情况下本地方法是一种用 Java 语言调用非 Java 语言代码的形式。本地方法目前只支持 C 和 C但是它们可以调用其他语言写的代码所以实际上可以调用任何代码。在非 Java 代码中也许会调用 C 的 malloc() 函数系列来分配存储空间而且除非调用 free() 函数不然存储空间永远得不到释放造成内存泄露。但是free() 是 C 和 C 中的函数所以你需要在 finalize() 方法里用本地方法调用它。 读到这里你可能明白了不会过多使用 finalize() 方法。对它确实不是进行普通的清理工作的合适场所。那么普通的清理工作在哪里执行呢 你必须实施清理 要清理一个对象用户必须在需要清理的时候调用执行清理动作的方法。这听上去相当直接但却与 C 中的析构函数的概念稍有抵触。在 C 中所有对象都会被销毁或者说应该被销毁。如果在 C 中创建了一个局部对象在栈上创建在 Java 中不行此时的销毁动作发生在以右花括号为边界的、此对象作用域的末尾处。如果对象是用 new 创建的类似于 Java 中那么当程序员调用 C 的 delete 操作符时Java 中不存在就会调用相应的析构函数。如果程序员忘记调用 delete那么永远不会调用析构函数这样就会导致内存泄露对象的其他部分也不会得到清理。这种 bug 很难跟踪也是让 C 程序员转向 Java 的一个主要因素。相反在 Java 中没有用于释放对象的 delete因为垃圾回收器会帮助你释放存储空间。甚至可以肤浅地认为正是由于垃圾回收的存在使得 Java 没有析构函数。然而随着学习的深入你会明白垃圾回收器的存在并不能完全替代析构函数而且绝对不能直接调用 finalize()所以这也不是一种解决方案。如果希望进行除释放存储空间之外的清理工作还是得明确调用某个恰当的 Java 方法这就等同于使用析构函数了只是没有它方便。 记住无论是垃圾回收还是终结都不保证一定会发生。如果 Java 虚拟机JVM并未面临内存耗尽的情形它可能不会浪费时间执行垃圾回收以恢复内存。 终结条件 通常不能指望 finalize() 你必须创建其他的清理方法并明确地调用它们。所以看起来finalize() 只对大部分程序员很难用到的一些晦涩内存清理里有用了。但是finalize() 还有一个有趣的用法它不依赖于每次都要对 finalize() 进行调用这就是对象终结条件的验证。 当对某个对象不感兴趣时——也就是它将被清理了这个对象应该处于某种状态这种状态下它占用的内存可以被安全地释放掉。例如如果对象代表了一个打开的文件在对象被垃圾回收之前程序员应该关闭这个文件。只要对象中存在没有被适当清理的部分程序就存在很隐晦的 bug。finalize() 可以用来最终发现这个情况尽管它并不总是被调用。如果某次 finalize() 的动作使得 bug 被发现那么就可以据此找出问题所在——这才是人们真正关心的。以下是个简单的例子示范了 finalize() 的可能使用方式 // housekeeping/TerminationCondition.java // Using finalize() to detect a object that // hasnt been properly cleaned upimport onjava.*;class Book {boolean checkedOut false;Book(boolean checkOut) {checkedOut checkOut;}void checkIn() {checkedOut false;}Overrideprotected void finalize() throws Throwable {if (checkedOut) {System.out.println(Error: checked out);}// Normally, youll also do this:// super.finalize(); // Call the base-class version} }public class TerminationCondition {public static void main(String[] args) {Book novel new Book(true);// Proper cleanup:novel.checkIn();// Drop the reference, forget to clean up:new Book(true);// Force garbage collection finalization:System.gc();new Nap(1); // One second delay}}输出 Error: checked out本例的终结条件是所有的 Book 对象在被垃圾回收之前必须被登记。但在 main() 方法中有一本书没有登记。要是没有 finalize() 方法来验证终结条件将会很难发现这个 bug。 你可能注意到使用了 Override。 意味着这是一个注解注解是关于代码的额外信息。在这里该注解告诉编译器这不是偶然地重定义在每个对象中都存在的 finalize() 方法——程序员知道自己在做什么。编译器确保你没有拼错方法名而且确保那个方法存在于基类中。注解也是对读者的提醒Override 在 Java 5 引入在 Java 7 中改善本书通篇会出现。 注意System.gc() 用于强制进行终结动作。但是即使不这么做只要重复地执行程序假设程序将分配大量的存储空间而导致垃圾回收动作的执行最终也能找出错误的 Book 对象。 你应该总是假设基类版本的 finalize() 也要做一些重要的事情使用 super 调用它就像在 Book.finalize() 中看到的那样。本例中它被注释掉了因为它需要进行异常处理而我们到现在还没有涉及到。 垃圾回收器如何工作 如果你以前用过的语言在堆上分配对象的代价十分高昂你可能自然会觉得 Java 中所有对象基本类型除外在堆上分配的方式也十分高昂。然而垃圾回收器能很明显地提高对象的创建速度。这听起来很奇怪——存储空间的释放影响了存储空间的分配但这确实是某些 Java 虚拟机的工作方式。这也意味着Java 从堆空间分配的速度可以和其他语言在栈上分配空间的速度相媲美。 例如你可以把 C 里的堆想象成一个院子里面每个对象都负责管理自己的地盘。一段时间后对象可能被销毁但地盘必须复用。在某些 Java 虚拟机中堆的实现截然不同它更像一个传送带每分配一个新对象它就向前移动一格。这意味着对象存储空间的分配速度特别快。Java 的堆指针只是简单地移动到尚未分配的区域所以它的效率与 C 在栈上分配空间的效率相当。当然实际过程中在簿记工作方面还有少量额外开销但是这部分开销比不上查找可用空间开销大。 你可能意识到了Java 中的堆并非完全像传送带那样工作。要是那样的话势必会导致频繁的内存页面调度——将其移进移出硬盘因此会显得需要拥有比实际需要更多的内存。页面调度会显著影响性能。最终在创建了足够多的对象后内存资源被耗尽。其中的秘密在于垃圾回收器的介入。当它工作时一边回收内存一边使堆中的对象紧凑排列这样堆指针就可以很容易地移动到更靠近传送带的开始处也就尽量避免了页面错误。垃圾回收器通过重新排列对象实现了一种高速的、有无限空间可分配的堆模型。 要想理解 Java 中的垃圾回收先了解其他系统中的垃圾回收机制将会很有帮助。一种简单但速度很慢的垃圾回收机制叫做_引用计数_。每个对象中含有一个引用计数器每当有引用指向该对象时引用计数加 1。当引用离开作用域或被置为 null 时引用计数减 1。因此管理引用计数是一个开销不大但是在程序的整个生命周期频繁发生的负担。垃圾回收器会遍历含有全部对象的列表当发现某个对象的引用计数为 0 时就释放其占用的空间但是引用计数模式经常会在计数为 0 时立即释放对象。这个机制存在一个缺点如果对象之间存在循环引用那么它们的引用计数都不为 0就会出现应该被回收但无法被回收的情况。对垃圾回收器而言定位这样的循环引用所需的工作量极大。引用计数常用来说明垃圾回收的工作方式但似乎从未被应用于任何一种 Java 虚拟机实现中。 在更快的策略中垃圾回收器并非基于引用计数。它们依据的是对于任意活的对象一定能最终追溯到其存活在栈或静态存储区中的引用。这个引用链条可能会穿过数个对象层次由此如果从栈或静态存储区出发遍历所有的引用你将会发现所有活的对象。对于发现的每个引用必须追踪它所引用的对象然后是该对象包含的所有引用如此反复进行直到访问完根源于栈或静态存储区的引用所形成的整个网络。你所访问过的对象一定是活的。注意这解决了对象间循环引用的问题这些对象不会被发现因此也就被自动回收了。 在这种方式下Java 虚拟机采用了一种_自适应_的垃圾回收技术。至于如何处理找到的存活对象取决于不同的 Java 虚拟机实现。其中有一种做法叫做停止-复制stop-and-copy。顾名思义这需要先暂停程序的运行不属于后台回收模式然后将所有存活的对象从当前堆复制到另一个堆没有复制的就是需要被垃圾回收的。另外当对象被复制到新堆时它们是一个挨着一个紧凑排列然后就可以按照前面描述的那样简单、直接地分配新空间了。 当对象从一处复制到另一处所有指向它的引用都必须修正。位于栈或静态存储区的引用可以直接被修正但可能还有其他指向这些对象的引用它们在遍历的过程中才能被找到可以想象成一个表格将旧地址映射到新地址。 这种所谓的复制回收器效率低下主要因为两个原因。其一得有两个堆然后在这两个分离的堆之间来回折腾得维护比实际需要多一倍的空间。某些 Java 虚拟机对此问题的处理方式是按需从堆中分配几块较大的内存复制动作发生在这些大块内存之间。 其二在于复制本身。一旦程序进入稳定状态之后可能只会产生少量垃圾甚至没有垃圾。尽管如此复制回收器仍然会将所有内存从一处复制到另一处这很浪费。为了避免这种状况一些 Java 虚拟机会进行检查要是没有新垃圾产生就会转换到另一种模式即自适应。这种模式称为标记-清扫mark-and-sweepSun 公司早期版本的 Java 虚拟机一直使用这种技术。对一般用途而言标记-清扫方式速度相当慢但是当你知道程序只会产生少量垃圾甚至不产生垃圾时它的速度就很快了。 标记-清扫所依据的思路仍然是从栈和静态存储区出发遍历所有的引用找出所有存活的对象。但是每当找到一个存活对象就给对象设一个标记并不回收它。只有当标记过程完成后清理动作才开始。在清理过程中没有标记的对象将被释放不会发生任何复制动作。标记-清扫后剩下的堆空间是不连续的垃圾回收器要是希望得到连续空间的话就需要重新整理剩下的对象。 停止-复制指的是这种垃圾回收动作不是在后台进行的相反垃圾回收动作发生的同时程序将会暂停。在 Oracle 公司的文档中会发现许多参考文献将垃圾回收视为低优先级的后台进程但是早期版本的 Java 虚拟机并不是这么实现垃圾回收器的。当可用内存较低时垃圾回收器会暂停程序。同样标记-清扫工作也必须在程序暂停的情况下才能进行。 如前文所述这里讨论的 Java 虚拟机中内存分配以较大的块为单位。如果对象较大它会占用单独的块。严格来说停止-复制要求在释放旧对象之前必须先将所有存活对象从旧堆复制到新堆这导致了大量的内存复制行为。有了块垃圾回收器就可以把对象复制到废弃的块。每个块都有年代数来记录自己是否存活。通常如果块在某处被引用其年代数加 1垃圾回收器会对上次回收动作之后新分配的块进行整理。这对处理大量短命的临时对象很有帮助。垃圾回收器会定期进行完整的清理动作——大型对象仍然不会复制只是年代数会增加含有小型对象的那些块则被复制并整理。Java 虚拟机会监视如果所有对象都很稳定垃圾回收的效率降低的话就切换到标记-清扫方式。同样Java 虚拟机会跟踪标记-清扫的效果如果堆空间出现很多碎片就会切换回停止-复制方式。这就是自适应的由来你可以给它个啰嗦的称呼自适应的、分代的、停止-复制、标记-清扫式的垃圾回收器。 Java 虚拟机中有许多附加技术用来提升速度。尤其是与加载器操作有关的被称为即时Just-In-Time, JIT编译器的技术。这种技术可以把程序全部或部分翻译成本地机器码所以不需要 JVM 来进行翻译因此运行得更快。当需要装载某个类通常是创建该类的第一个对象时编译器会先找到其 .class 文件然后将该类的字节码装入内存。你可以让即时编译器编译所有代码但这种做法有两个缺点一是这种加载动作贯穿整个程序生命周期内累加起来需要花更多时间二是会增加可执行代码的长度字节码要比即时编译器展开后的本地机器码小很多这会导致页面调度从而一定降低程序速度。另一种做法称为_惰性评估_意味着即时编译器只有在必要的时候才编译代码。这样从未被执行的代码也许就压根不会被 JIT 编译。新版 JDK 中的 Java HotSpot 技术就采用了类似的做法代码每被执行一次就优化一些所以执行的次数越多它的速度就越快。
http://www.zqtcl.cn/news/20062/

相关文章:

  • html网站尺寸链接交换
  • 网站首页快照不更新衡水网站建
  • 网站怎么做伪静态页面做设计任务的网站
  • 天涯论坛网站建设怎么让百度收录网址
  • 做全世界的生意的网站住房和城乡建设部网站办事大厅里边
  • 注册网站会员需要详细杭州手机软件开发
  • 企业模板建站有哪些wordpress 创意
  • 免费申请公司网站网站建设的软件有哪些
  • 移动互联网站建设有赞微商城小程序
  • 汕头网站建设方案优化网页制作的教程视频
  • 建设一个最普通网站要多少钱宜昌的网站建设
  • 网站文章模板成都营销推广公司
  • 中小企业网站积木式搭建公司法全文
  • php在网站后台建设中的优势免费咨询服务合同范本
  • 谷歌建站多少钱青田县建设局网站
  • 长沙做网站有哪些重庆招标建设信息网站
  • 南京铁路建设网站安卓程序开发
  • 门户网站建设开发企业可以做哪些网站
  • 网站推广经典案例公众号文章排版
  • 平台网站怎么建设东莞网站建设方案托管
  • 罗湖网站建设58本地电脑做网站服务器
  • 自己做付费网站做体力活的网站
  • 创研科技网站WordPress qinmei影视主题
  • 关于小城镇建设的网站单位装专用的网站网页归档
  • 电子商务综合实训报告网站建设做视频网站违法
  • 淄博企业网站建设网站建站网站制作公司
  • centos建设网站东莞三网合一网站制作
  • 石家庄中小企业网站制作上海网上推广优化
  • 提供网站建设设计外包php自己做网站吗
  • 网站网站平台建设方案影视投资销售怎么找客户