个人做网站名称可以随意更改吗,如何申请建设网站域名,深圳华强北水货手机报价,怎么做同学录的网站文章目录 概要对象存活判断引用计数算法可达性分析算法对象是否存活各种引用 垃圾收集算法分代收集理论复制算法标记清除算法标记-整理算法 概要
垃圾收集#xff08;Garbage Collection#xff0c; 下文简称GC#xff09;#xff0c;其优缺点如下#xff1a; 优点#… 文章目录 概要对象存活判断引用计数算法可达性分析算法对象是否存活各种引用 垃圾收集算法分代收集理论复制算法标记清除算法标记-整理算法 概要
垃圾收集Garbage Collection 下文简称GC其优缺点如下 优点
不需要考虑内存管理可以有效的防止内存泄漏有效的利用可使用的内存
缺点
java开发人员不了解自动内存管理, 内存管理就像一个黑匣子,过度依赖就会降低我们解决内存溢出/内存泄漏等问题的能力。
对象存活判断
引用计数算法
引用计数算法可以这样实现给每个创建的对象添加一个引用计数器每当此对象被某个地方引用时计数值1引用失效时-1所以当计数值为0时表示对象已经不能被使用。引用计数算法大多数情况下是个比较不错的算法简单直接也有一些著名的应用案例但是对于Java虚拟机来说并不是一个好的选择因为它很难解决对象直接相互循环引用的问题。
优点 实现简单执行效率高很好的和程序交织。 缺点 无法检测出循环引用。
可达性分析算法
在主流的商用程序语言如Java、C#等的主流实现中都是通过可达性分析(Reachability Analysis)来判断对象是否存活的。此算法的基本思路就是通过一系列的“GC Roots”的对象作为起始点从起始点开始向下搜索到对象的路径。搜索所经过的路径称为引用链(Reference Chain)当一个对象到任何GC Roots都没有引用链时则表明对象“不可达”即该对象是不可用的。 在Java语言中可作为GC Roots的对象包括下面几种
栈帧中的局部变量表中的reference引用所引用的对象方法区中static静态引用的对象方法区中final常量引用的对象本地方法栈中JNI(Native方法)引用的对象Java虚拟机内部的引用 如基本数据类型对应的Class对象 一些常驻的异常对象比如 NullPointExcepiton、 OutOfMemoryError 等 还有系统类加载器。所有被同步锁synchronized关键字 持有的对象反映Java虚拟机内部情况的JMXBean、 JVMTI中注册的回调、 本地代码缓存等 对象是否存活
finalize()方法最终判定对象是否存活:
即使在可达性分析算法中判定为不可达的对象 也不是“非死不可”的 这时候它们暂时还处于“缓刑”阶段 要真 正宣告一个对象死亡 至少要经历两次标记过程
第一次标记: 如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链 那它将会被第一次标记 随后进行一次筛选 筛选的条件是此对象是否有必要执行finalize()方法。 没有必要: 假如对象没有覆盖finalize()方法 或者finalize()方法已经被虚拟机调用过 那么虚拟机将这两种情况都视为“没有必要执行”。 有必要:
如果这个对象被判定为确有必要执行finalize()方法 那么该对象将会被放置在一个名为F-Queue的 队列之中 并在 稍后由一条由虚拟机自动建立的、 低调度优先级的Finalizer线程去执行它们的finalize() 方法。 finalize()方法是对 象 逃脱死亡命运的最后一次机会 稍后收集器将对F-Queue中的对象进行第二次小规模的标记 如果对 象要在 finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可 譬如把自己 this关键字 赋值 给某个类变量或者对象的成员变量 那在第二次标记时它将被移出“即将回收”的集 合 如果对象这时候还没有逃 脱 那基本上它就真的要被回收了。 Finalizer线程去执行它们的finalize() 方法, 这里所说的“执行”是指虚拟机会触发这个方法开始运行 但并不承诺一定会等待它运行结束。 这样做的原因是 如果某个对象的finalize()方法执行缓慢 或者更极端地发生了死循环 将很可能导 致F-Queue队列中的其他对象永久处于等待 甚至导致整个内存回收子系统的崩溃。
各种引用
在JDK1.2之后Java对引用的概念做了扩充将引用分为 强引用(Strong Reference) 、 软引用(Soft Reference) 、 弱引用(Weak Reference) 和 虚引用(Phantom Reference) 四种这四种引用的强度依次递减。 强引用是使用最普遍的引用。如果一个对象具有强引用那垃圾回收器绝不会回收它。当内存空间不足Java虚拟 机宁愿抛出OutOfMemoryError错误使程序异常终止也不会靠随意回收具有强引用的对象来解决内存不足的问 题。 ps强引用其实也就是我们平时A a new A()这个意思。 软引用: 如果一个对象只具有软引用则内存空间足够垃圾回收器就不会回收它如果内存空间不足了就会回收这些对象的内存。只要垃圾回收器没有回收它该对象就可以被程序使用。 软引用可以和一个引用队列 ReferenceQueue联合使用如果软引用所引用的对象被垃圾回收器回收Java虚拟机就会把这个软引用加入到与之关联的引用队列中。 弱引用:用来描述那些非必须对象。 当垃圾收集器开始工作 无论当前内存是否足够 都会回收掉只被弱引用关联的对象。 在JDK 1.2版之后提供了WeakReference类来实现弱引用。 弱引用可以和一个引用队列ReferenceQueue联合使用如果弱引用所引用的对象被垃圾回收Java虚拟机就会把这个弱引用加入到与之关联的引用队列中. 虚引用一个对象仅持有虚引用在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动 虚引用与软引用和弱引用的一个区别在于 1、引用必须和引用队列 ReferenceQueue联合使用。 2、当垃圾回收器准备回收一个对象时如果发现它还有虚引用就会在回收对象的内存之前把这个虚引用加入到 与之 关联的引用队列中。
垃圾收集算法
分代收集理论
当前商业虚拟机的垃圾回收器大多遵循“分代收集”的理论来进行设计这个理论大体上是这么描述的
绝大部分的对象都是朝生夕死。熬过多次垃圾回收的对象就越难回收
根据以上两个理论朝生夕死的对象放一个区域难回收的对象放另外一个区域这个就构成了新生代和老年代并且不同的分代采用的回收算法不一样。 同时对于GC的叫法大体有这么几种 1、新生代回收 Minor GC/Young GC :指的是进行新生代的回收。 2、老年代回收 Major GC/Old GC :指的是进行老年代的回收。目前只有 CMS 垃圾回收器会有这个单独的回收老年代的行为。 Major GC 定义相对没有那么明确有说指是老年代有的说是做整个堆的收集没有固定的说法有时候 Major GC 和 Full GC 大致是等价的 3、整堆回收 Full GC 定义相对明确收集整个 Java 堆和方法区注意包含方法区
复制算法
原始的复制算法Copying是这样的
将内存按容量划分为大小相等的两块每次只使用其中的一块。当其中一块内存用完了就将还存活着的对象复制到另外一块上面然后再把已使用过的内存空间一次清理掉。 优点
实现简单运行高效每次都是对整个半区进行内存回收内存分配时也就不用考虑内存碎片等复杂情况只要按顺序分配内存即可
缺点
内存的使用率缩小为原来的一半。内存移动是必须实打实的移动复制所以对应的引用直接指针需要调整。
适用场景 复制回收算法适合于新生代因为大部分对象朝生夕死那么复制过去的对象比较少效率自然就高另外一半的一次性清理是很快的。
像 hotspot 这样的虚拟机大都对原生的复制算法进行了改进因为它对内存空间的利用率不高而且专门研究表明新生代中的对象 98% 是“朝生夕死”的所以并不需要按照 1:1 的比例来划分内存空间所以改进后的复制算法策略是
1、将新生代划分为一块较大的 Eden 区和两块较小的 Survivor 空间你可以叫做 From 或者 To HotSpot 虚拟机默认 Eden 和 Survivor 的大小比例是 8:1 。 2、每次使用 Eden 和其中一块 Survivor 当回收时将 Eden 和Survivor 中还存活着的对象一次性地复制到另外一块 Survivor 空间上最后清理掉 Eden 和刚才用过的 Survivor 空间。
在这样的算法下 1、每次新生代中可用内存空间为整个新生代容量的 90%80%10%只有 10%的内存会被 “浪费” 2、当然98%的对象可回收只是一般场景下的数据我们没有办法保证每次回收都只有不多于 10%的对象存活当 Survivor 空间不够用时需要依赖其他内存老年代进行分配担保 Handle Promotion 。
标记清除算法
标记-清除Mark-Sweep算法分为“标记”和“清除”两个阶段
首先扫描所有对象标记出需要回收的对象在标记完成后扫描并回收所有被标记的对象故需要两次扫描 注意 1、回收效率略低如果大部分对象是朝生夕死那么回收效率降低因为需要大量标记对象和回收对象对比复制回收效率要低所以该算法不适合新生代。 2、它的主要问题是在标记清除之后会产生大量不连续的内存碎片空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾回收动作。 3、标记清除算法适用于老年代。
标记-整理算法
标记-整理Mark-Compact算法逻辑如下 1、首先标记出所有需要回收的对象 2、在标记完成后后续步骤不是直接对可回收对象进行清理而是让所有存活的对象都向一端移动 3、然后直接清理掉端边界以外的内存。 注意 1、标记整理需要扫描两遍 2、标记整理与标记清除算法的区别主要在于对象的移动。对象移动不单单会加重系统负担同时需要全程暂停用户线程才能进行同时所有引用对象的地方都需要更新直接指针需要调整。 3、标记整理算法不会产生内存碎片但是效率偏低。 4、标记整理算法适用于老年代。
所以看到老年代采用的标记整理算法与标记清除算法各有优点各有缺点。