Excel不相邻列如何打印在一起-英雄云拓展知识分享
105
2023-11-07
【摘要】 本书摘自《深入理解 Java 虚拟机 JVM 高级特性与最佳实践(第3版)》一书中第3章,第1节,周志明著。
第3章 垃圾收集器与内存分配策略
Java 与 C++ 之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想 进去,墙里面的人却想出来。
3.1 概述
说起垃圾收集 (Garbage Collection, 下文简称 GC), 有不少人把这项技术当作 Java 语 言的伴生产物。事实上,垃圾收集的历史远远比Java 久远,在1960年诞生于麻省理工学 院的Lisp 是第一门开始使用内存动态分配和垃圾收集技术的语言。当Lisp 还在胚胎时期 时,其作者 John McCarthy 就思考过垃圾收集需要完成的三件事情:
口哪些内存需要回收?
口什么时候回收?
口如何回收?
经过半个世纪的发展,今天的内存动态分配与内存回收技术已经相当成熟, 一切看起 来都进入了“自动化”时代,那为什么我们还要去了解垃圾收集和内存分配?答案很简单: 当需要排查各种内存溢出、内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈 时,我们就必须对这些“自动化”的技术实施必要的监控和调节。
把时间从大半个世纪以前拨回到现在,舞台也回到我们熟悉的Java 语言。第2章介绍 了Java 内存运行时区域的各个部分,其中程序计数器、虚拟机栈、本地方法栈3个区域随 线程而生,随线程而灭,栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入 栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的(尽管在运行期知的),因此这几个区域的内存分配和回收都具备确定性,在这几个区域内就不需要过多考 虑如何回收的问题,当方法结束或者线程结束时,内存自然就跟随着回收了。
而Java 堆和方法区这两个区域则有着很显著的不确定性: 一个接口的多个实现类需要 的内存可能会不一样, 一个方法所执行的不同条件分支所需要的内存也可能不一样,只有 处于运行期间,我们才能知道程序究竟会创建哪些对象,创建多少个对象,这部分内存的 分配和回收是动态的。垃圾收集器所关注的正是这部分内存该如何管理,本文后续讨论中 的“内存”分配与回收也仅仅特指这一部分内存。
3.2 对象已死?
在堆里面存放着Java 世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第 一件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”(“死去”即不可能 再被任何途径使用的对象)了。
3.2.1 引用计数算法
很多教科书判断对象是否存活的算法是这样的:在对象中添加一个引用计数器,每当 有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数 器为零的对象就是不可能再被使用的。笔者面试过很多应届生和一些有多年工作经验的开 发人员,他们对于这个问题给予的都是这个答案。
客观地说,引用计数算法 (Reference Counting) 虽然占用了一些额外的内存空间来 进行计数,但它的原理简单,判定效率也很高,在大多数情况下它都是一个不错的算法。 也有一些比较著名的应用案例,例如微软 COM(Component Object Model) 技术、使用 ActionScript3 的 FlashPlayer 、Python 语言以及在游戏脚本领域得到许多应用的 Squirrel 中 都使用了引用计数算法进行内存管理。但是,在Java 领域,至少主流的 Java 虚拟机里面都 没有选用引用计数算法来管理内存,主要原因是,这个看似简单的算法有很多例外情况要 考虑,必须要配合大量额外处理才能保证正确地工作,譬如单纯的引用计数就很难解决对 象之间相互循环引用的问题。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们 18664393530@aliyun.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~