知识大全 对象引用是怎样严重影响垃圾收集器
Posted 变量
篇首语:追光的人,终会万丈光芒。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 对象引用是怎样严重影响垃圾收集器相关的知识,希望对你有一定的参考价值。
如果您认为 Java 游戏开发人员是 Java 编程世界的一级方程式赛车手 那么您就会明白为什么他们会如此地重视程序的性能 游戏开发人员几乎每天都要面对的性能问题 往往超过了一般程序员考虑问题的范围 哪里可以找到这些特殊的开发人员呢?Java 游戏社区就是一个好去处(参见 参考资料) 虽然在这个站点可能没有很多关于服务器端的应用 但是我们依然可以从中受益 看看这些 惜比特如金 的游戏开发人员每天所面对的 我们往往能从中得到宝贵的经验 让我们开始游戏吧! 对象泄漏 游戏程序员跟其他程序员一样??他们也需要理解 Java 运行时环境的一些微妙之处 比如垃圾收集 垃圾收集可能是使您感到难于理解的较难的概念之一 因为它并不能总是毫无遗漏地解决 Java 运行时环境中堆管理的问题 似乎有很多类似这样的讨论 它的开头或结尾写着 我的问题是关于垃圾收集 假如您正面遭遇内存耗尽(out of memory)的错误 于是您使用检测工具想要找到问题所在 但这是徒劳的 您很容易想到另外一个比较可信的原因 这是 Java 虚拟机堆管理的问题 而不会认为这是您自己的程序的缘故 但是 正如 Java 游戏社区的资深专家不止一次地解释的 Java 虚拟机并不存在任何被证实的对象泄漏问题 实践证明 垃圾收集器一般能够精确地判断哪些对象可被收集 并且重新收回它们的内存空间给 Java 虚拟机 所以 如果您遇到了内存耗尽的错误 那么这完全可能是由您的程序造成的 也就是说您的程序中存在着 无意识的对象保留(unintentional object retention) 内存泄漏与无意识的对象保留 内存泄漏和无意识的对象保留的区别是什么呢?对于用 Java 语言编写的程序来说 确实没有区别 两者都是指在您的程序中存在一些对象引用 但实际上您并不需要引用这些对象 一个典型的例子是向一个集合中加入一些对象以便以后使用它们 但是您却忘了在使用完以后从集合中删除这些对象 因为集合可以无限制地扩大 并且从来不会变小 所以当您在集合中加入了太多的对象(或者是有很多的对象被集合中的元素所引用)时 您就会因为堆的空间被填满而导致内存耗尽的错误 垃圾收集器不能收集这些您认为已经用完的对象 因为对于垃圾收集器来说 应用程序仍然可以通过这个集合在任何时候访问这些对象 所以这些对象是不可能被当作垃圾的 对于没有垃圾收集的语言来说 例如 C++ 内存泄漏和无意识的对象保留是有区别的 C++ 程序跟 Java 程序一样 可能产生无意识的对象保留 但是 C++ 程序中存在真正的内存泄漏 即应用程序无法访问一些对象以至于被这些对象使用的内存无法释放且返还给系统 令人欣慰的是 在 Java 程序中 这种内存泄漏是不可能出现的 所以 我们更喜欢用 无意识的对象保留 来表示这个令 Java 程序员抓破头皮的内存问题 这样 我们就能区别于其他使用没有垃圾收集语言的程序员 跟踪被保留的对象 那么当发现了无意识的对象保留该怎么办呢?首先 需要确定哪些对象是被无意保留的 并且需要找到究竟是哪些对象在引用它们 然后必须安排好 应该在哪里释放它们 最容易的方法是使用能够对堆产生快照的检测工具来标识这些对象 比较堆的快照中对象的数目 跟踪这些对象 找到引用这些对象的对象 然后强制进行垃圾收集 有了这样一个检测器 接下来的工作相对而言就比较简单了: 等待直到系统达到一个稳定的状态 这个状态下大多数新产生的对象都是暂时的 符合被收集的条件 这种状态一般在程序所有的初始化工作都完成了之后 强制进行一次垃圾收集 并且对此时的堆做一份对象快照 进行任何可以产生无意地保留的对象的操作 再强制进行一次垃圾收集 然后对系统堆中的对象做第二次对象快照 比较两次快照 看看哪些对象的被引用数量比第一次快照时增加了 因为您在快照之前强制进行了垃圾收集 那么剩下的对象都应该是被应用程序所引用的对象 并且通过比较两次快照我们可以准确地找出那些被程序保留的 新产生的对象 根据您对应用程序本身的理解 并且根据对两次快照的比较 判断出哪些对象是被无意保留的 跟踪这些对象的引用链 找出究竟是哪些对象在引用这些无意地保留的对象 直到您找到了那个根对象 它就是产生问题的根源 显式地赋空(nulling)变量 一谈到垃圾收集这个主题 总会涉及到这样一个吸引人的讨论 即显式地赋空变量是否有助于程序的性能 赋空变量是指简单地将 null 值显式地赋值给这个变量 相对于让该变量的引用失去其作用域 清单 局部作用域 public static String scopingExample(String string) StringBuffer sb = new StringBuffer(); sb append( hello ) append(string); sb append( nice to see you! ); return sb toString(); 当该方法执行时 运行时栈保留了一个对 StringBuffer 对象的引用 这个对象是在程序的第一行产生的 在这个方法的整个执行期间 栈保存的这个对象引用将会防止该对象被当作垃圾 当这个方法执行完毕 变量 sb 也就失去了它的作用域 相应地运行时栈就会删除对该 StringBuffer 对象的引用 于是不再有对该 StringBuffer 对象的引用 现在它就可以被当作垃圾收集了 栈删除引用的操作就等于在该方法结束时将 null 值赋给变量 sb 错误的作用域 既然 Java 虚拟机可以执行等价于赋空的操作 那么显式地赋空变量还有什么用呢?对于在正确的作用域中的变量来说 显式地赋空变量的确没用 但是让我们来看看另外一个版本的 scopingExample 方法 这一次我们将把变量 sb 放在一个错误的作用域中 清单 静态作用域 static StringBuffer sb = new StringBuffer(); public static String scopingExample(String string) sb = new StringBuffer(); sb append( hello ) append(string); sb append( nice to see you! ); return sb toString(); 现在 sb 是一个静态变量 所以只要它所在的类还装载在 Java 虚拟机中 它也将一直存在 该方法执行一次 一个新的 StringBuffer 将被创建并且被 sb 变量引用 在这种情况下 sb 变量以前引用的 StringBuffer 对象将会死亡 成为垃圾收集的对象 也就是说 这个死亡的 StringBuffer 对象被程序保留的时间比它实际需要保留的时间长得多??如果再也没有对该 scopingExample 方法的调用 它将会永远保留下去 一个有问题的例子 即使如此 显式地赋空变量能够提高性能吗?我们会发现我们很难相信一个对象会或多或少对程序的性能产生很大影响 直到我看到了一个在 Java Games 的 Sun 工程师给出的一个例子 这个例子包含了一个不幸的大型对象 清单 仍在静态作用域中的对象 private static Object bigObject; public static void test(int size) long startTime = System currentTimeMillis(); long numObjects = ; while (true) //bigObject = null; //explicit nulling //SizableObject could simply be a large array e g byte[] //In the JavaGaming discussion it was a BufferedImage bigObject = new SizableObject(size); long endTime = System currentTimeMillis(); ++numObjects; // We print stats for every o seconds if (endTime startTime >= ) System out println( Objects created per seconds = + numObjects); startTime = endTime; numObjects = ; 这个例子有个简单的循环 创建一个大型对象并且将它赋给同一个变量 每隔两秒钟报告一次所创建的对象个数 现在的 Java 虚拟机采用 generational 垃圾收集机制 新的对象创建之后放在一个内存空间(取名 Eden)内 然后将那些在第一次垃圾收集以后仍然保留的对象转移到另外一个内存空间 在 Eden 即创建新对象时所在的新一代空间中 收集对象要比在 老一代 空间中快得多 但是如果 Eden 空间已经满了 没有空间可供分配 那么就必须把 Eden 中的对象转移到老一代空间中 腾出空间来给新创建的对象 如果没有显式地赋空变量 而且所创建的对象足够大 那么 Eden 就会填满 并且垃圾收集器就不能收集当前所引用的这个大型对象 所产生的后果是 这个大型对象被转移到 老一代空间 并且要花更多的时间来收集它 通过显式地赋空变量 Eden 就能在新对象创建之前获得自由空间 这样垃圾收集就会更快 实际上 在显式赋空的情况下 该循环在两秒钟内创建的对象个数是没有显式赋空时的 倍??但是仅当您选择创建的对象要足够大而可以填满 Eden 时才是如此 在 Windows 环境 Java虚拟机 的默认配置下大概需要 KB 那就是一行赋空操作产生的 倍的性能差距 但是请注意这个性能差别产生的原因是变量的作用域不正确 这正是赋空操作发挥作用的地方 并且是因为所创建的对象非常大 最佳实践 这是一个有趣的例子 但是值得强调的是 cha138/Article/program/Java/hx/201311/26746相关参考
停止和复制 标记和清除法的兄弟就是停止和复制收集法了停止和复制法解决了标记和清除法的碎片问题但是对内存提出了更高的要求(或者是对一个较小的对象池进行更加频繁的收集)微软的Java虚拟机使用的就是
:新生代串行收集器(默认收集器) 算法复制算法 XX:+UseSerialGC指定使用新生代串行收集器和老年代串行收集器 优点效率高久经考验 缺点串行如果回收对象过多或者堆过大停顿时间会过
垃圾分类是指按照垃圾的不同成分、属性、利用价值以及对环境的影响,并根据不同处置方式的要求,分成属性不同的若干种类。垃圾分类收集是指垃圾按其处置的性能或可利用的价值而分别收集的方式,其目的是为资源回收和
垃圾分类是指按照垃圾的不同成分、属性、利用价值以及对环境的影响,并根据不同处置方式的要求,分成属性不同的若干种类。垃圾分类收集是指垃圾按其处置的性能或可利用的价值而分别收集的方式,其目的是为资源回收和
垃圾分类是指按照垃圾的不同成分、属性、利用价值以及对环境的影响,并根据不同处置方式的要求,分成属性不同的若干种类。垃圾分类收集是指垃圾按其处置的性能或可利用的价值而分别收集的方式,其目的是为资源回收和
Java不具备象C++的破坏器那样的概念在C++中一旦破坏(清除)一个对象就会自动调用破坏器方法之所以将其省略大概是由于在Java中只需简单地忘记对象不需强行破坏它们垃圾收集器会在必要的时候自动回
工程特点:渗滤液调节池是将垃圾处理场运营过程中产生的垃圾渗滤液,通过收集系统收集起来集中处理的池体。这些池子一般都是暴露在露天环境中,长期受到暴晒、严寒以及严重的冲刷及腐蚀,影响其使用寿命。锄禾环保使
工程特点:渗滤液调节池是将垃圾处理场运营过程中产生的垃圾渗滤液,通过收集系统收集起来集中处理的池体。这些池子一般都是暴露在露天环境中,长期受到暴晒、严寒以及严重的冲刷及腐蚀,影响其使用寿命。锄禾环保使
工程特点:渗滤液调节池是将垃圾处理场运营过程中产生的垃圾渗滤液,通过收集系统收集起来集中处理的池体。这些池子一般都是暴露在露天环境中,长期受到暴晒、严寒以及严重的冲刷及腐蚀,影响其使用寿命。锄禾环保使
怎样破解垃圾围城困境?一、完善垃圾处理管理体制。目前垃圾处理管理体制基本上还是过去按照末端无害化处理要求建立的,主要包括垃圾收集和填埋焚烧等环节,减量化和资源化环节的管理比较薄弱。二、做好垃圾处理场所