垃圾回收方法

110 阅读5分钟

[TOC]

垃圾收集算法

1.引用计数法

a,b相互引用之后置空就会导致内存泄漏

2.可达性分析算法

根搜索算法 GC Roots Tracing

从一系列名为GC Root的对象作为起始点,所这些节点向下搜索,tracing不到的对象则被视为不可达

GC root

  1. 虚拟机栈中的引用的对象
  2. 方法区中的类静态属性引用的对象
  3. 方法区中的常量引用的对象
  4. 本地方法栈中JNI的引用的对象

引用

java的引用分为强引用,软引用,弱引用,虚引用

哪些对象可以作为GC Root

判定死亡的条件

从GC root开始遍历无法遍历到,会被第一次标记并进行一次筛选 ,筛选的条件是是否有必要执行finalize()方法。如果对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,将被视为“没有必要执行”

垃圾回收算法

标记清除算法

标记需要清楚的对象,然后进行删除。 效率不够高,并且会产生内存碎片

复制算法

先把对象都存储到一个内存区域 进行垃圾回收时,把或者的对象复制到另一片区域 然后直接把之前的内存区域清除为0 这两篇区域的大小一般是8:1

标记-整理算法

让所有的对象都朝一端移动(好奇怪的形容词) 类似让一张纸上的蚂蚁都爬到上半部分,然后把下半部分清理掉

分代收集算法

将内存分为新生代和老年代 新生代中,每次垃圾收集发现大批对象死去,就可以选用复制算法 老年代中对象存活率高,使用标记清楚方法,或者标记-整理方法

垃圾收集器

Serial收集器

ParNew收集器

Parrallel Scavenge 收集器

Serial Old 收集器

Parrallel Old 收集器

CMS 收集器

G1 收集器

基于标记-清理算法 M毫秒内垃圾收集的时间不超过N毫秒 G1将整个JAVA堆划分成多个大小固定的独立区域 跟踪他们的垃圾堆积程度,维护一个优先队列,优先回收垃圾最多的区域 所以叫它Gabarge First

1、并行于并发:G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短stop-The-World停顿时间。部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行。

2、分代收集:虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但是还是保留了分代的概念。它能够采用不同的方式去处理新创建的对象和已经存活了一段时间,熬过多次GC的旧对象以获取更好的收集效果。

3、空间整合:与CMS的“标记--清理”算法不同,G1从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。

4、可预测的停顿:这是G1相对于CMS的另一个大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,

5、G1运作步骤:

1、初始标记;2、并发标记;3、最终标记;4、筛选回收

上面几个步骤的运作过程和CMS有很多相似之处。初始标记阶段仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS的值,让下一个阶段用户程序并发运行时,能在正确可用的Region中创建新对象,这一阶段需要停顿线程,但是耗时很短

并发标记阶段是从GC Root开始对堆中对象进行可达性分析,找出存活的对象,这阶段时耗时较长,但可与用户程序并发执行。 而最终标记阶段则是为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程Remenbered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中,这一阶段需要停顿线程,但是可并行执行。 最后在筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。

内存分配和回收策略

对象优先在Eden区分配

如果eden区域内存不够,虚拟机就要来一次Minor GC

Minor GC 新生代GC

新生代的垃圾收集机制 回收垃圾比较快 使用复制收集算法

Major 老年代GC

什么对象是老年代

  • 大对象直接进入老年代

长期存活的对象将进入老年代

每个对象都定义了一个对象年龄计数器 经过一次MInor GC之后依然存活,便将年龄加1,当他的年龄超过15(默认),就被添加到老年代 也可以通过参数MaxTenuringThreshould来调整这个阈值

动态判定进入老年代

如果Survior区域内相同年龄的对象超过空间的一半,则年龄大于等于该年龄的对象可以直接进入老年代

内存泄漏

内存泄漏主要是有两种情况:

  1. 在堆中申请的空间没有被释放(在java中基本不会)
  2. 对象已经不再被使用,但是还在内存中保留

在java造成内存泄漏的原因主要有以下这些

  1. 静态集合类:HashMap 这些容器是静态的,生命周期和程序一致,容器中的对象在程序结束前不被释放,就会造成内存泄漏
  2. 对于数据库或者网络的连接没有关闭
  3. 监听器
  4. 变量的不合理作用范围