垃圾回收
2018.5.5 记录学习
1. 在内存运行的各个部分中,其中程序计数器、虚拟机栈、本地方法栈这三个区域随线程而生,随线程而灭。栈中的栈帧随着方法的进入和退出而执行进栈和出栈
操作,每个栈帧中分配多少内存基本上在类结构确定下来时就已知的。因此这几个区域的内存分配回收具有确定性。在方法结束和线程结束时,内存自然跟着
被回收。
2. Java 堆和方法区需要内存回收: 一个接口的多个实现类需要的内存不一样,一个方法中的多个分支需要的内存也不一样,我们只有在程序运行期间才知道会创建
哪些对象。
方法区: 又称永久代。垃圾回收主要回收两部分内容: 废弃常量和和无用的类。
1) 已常量池中字面量为例:若字符串abc已经进入常量池,而系统中没有任何String 对象引用该常量,也没有其他地方引用该字面量,
那么就认为该字符串可以回收,
2) 无用的类需要满足3个条件:
(1)该类所有的实例都已经被回收,即Java堆中不存在该类的任何实例;
(2)加载该类的ClassLoader已经被回收;
(3)该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
是否对类进行回收:-Xnoclassgc 参数控制。
垃圾收集算法(上面确认了什么需要回收,下面讲怎么进行回收):
各个平台的虚拟机操作内存的方法不同,只介绍几种算法思想 。
链接1: http://www.cnblogs.com/zhangchenglzhao/p/8922076.html
标记-清除算法 |
分为标记和清除两步:标记过程参照链接1,在标记完成后统一回收所有被标记的对象。 不足:标记和清除效率都不高; 会产生大量不连续的空间碎片。 |
复制算法 |
把内存分为两块,每次只使用其中的一块,当这块满了,就将还存活的对象复制到另一块,然后清空原来的那一块。可以解决内存碎片的问题。 不足:内存只能使用一半。所以通常用于对象存活率较低的情况,如果对象存活率较高,则复制也是要发费时间,效率变低。 通常用于新生代回收: 因为新生代的对象大多数存活事件较短,所以不需要按照1:1 的比例划分 。 而是讲内存分为一块较大的Eden 和两块较小的 survivor空间。每次只用eden和其中一块survivor . 回收时将eden和survivor中还存活的对象一次性的复制到 另一块survivor 上,最后清理掉eden和 刚才使用郭的survivor . Hotspot 默认比例是 8:1:1 。但是可能出现一种情况: 当survivor 放不下还存活的对象,那么就需要老年代做一个分配 担保,所以特别不适合对象存活率较高的空间。 |
标记整理算法 |
标记过程和标记清除算法一样。 但是后续不是直接清理可回收对象,而是将所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。 适合对象存活率较高的空间,如老年代。 |
分代收集算法 |
当前商业虚拟机的垃圾收集都采用 分代收集算法:根据对象存活周期的不同将内存分为几块。一般是将java堆分为新生代和老年代。 根据各自的特点 使用不同的收集算法:新生代存活率低,通常用复制算法。老年代通常用标记整理 或者标记清除。 |