首页
Web开发
Windows程序
编程语言
数据库
移动开发
系统相关
微信
其他好文
会员
首页
>
编程语言
> 详细
JVM垃圾回收机制与算法
时间:
2016-05-12 21:51:40
阅读:
256
评论:
0
收藏:
0
[点我收藏+]
标签:
JVM内存由几个部分组成:堆、方法区、栈、程序计数器、本地方法栈
JVM垃圾回收仅针对公共内存区域,即:堆和方法区进行,因为只有这两个区域在运行时才能知道需要创建些对象,其内存分配和回收都是动态的。
一、垃圾回收策略
1.1分代管理
将堆和方法区按照对象不同年龄进行分代:
(Young Generation and Old Generation)堆中会频繁创建对象,基于一种分代的思想,按照对象存活时间将堆划分为新生代和旧生代两部分,并不是一次垃圾回收新生代存活的对象就放入旧生代,
而是要经过几次GC后还存活的对象,才放入旧生代,所以把新生代再次划分为Eden区和两个Survivor区,让对象创建在Eden区,然后在两个Survivor之间反复复制,
最后仍然存活的对象才复制到旧生代中。
(Permanent Generation)方法区存放的是常量、加载的字节码文件信息等,信息相对稳定。因为不会频繁创建对象,所以不需要分代,直接GC即可。
新生代:
1.所有新对象创建发生在Eden区,Eden区满后触发新生代上的minor GC,将Eden区和非空闲Survivor区存活对象复制到另一个空闲的Survivor区中。
2.永远保证一个Survivor是空的,新生代minor GC就是两个Survivor区之间相互复制存活对象,直到Survivor区满为止。
旧生代:
1.Eden区满后触发minor GC将存活对象复制到Survivor区,Survivor区满后触发minor GC将存活对象复制到旧生代。
2.经过新生代的两个Survivor之间多次复制,仍然存活下来的对象就是年龄相对比较老的,可以放入旧生代了,随着时间推移,
如果旧生代也满了,将触发Full GC,针对整个堆(包括新生代、旧生代和持久代)进行垃圾回收。
持久代:
存储class类、常量、方法描述等,持久代如果满,将触发Full GC,回收废弃常量和无用的类
1.2垃圾回收
要执行垃圾回收,关键在于两点,一是检测垃圾对象,二是释放垃圾对象所占用的空间。
1.检测垃圾对象
1)引用计数法:
对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器就减1.
只要对象A的引用计数器的值为0,则对象A 就不可能再被使用。
实现时只需要为每个对象配置一个整型的计数器即可。
但是引用计数器有一个严重的问题,即无法处理循环引用的情况。
对象A中含有对象B的引用,对象B中含有对象A的引用。此时,对象A和B的引用计数器都不为0.但是在系统中却不存在任何第3个对象引用了A或B。
即,A和B是应该被回收的垃圾对象,但由于垃圾对象间相互引用,从而垃圾回收器无法识别,引起内存泄漏。
2)可达性分析:
引用计数法无法检测对象之间相互循环引用的问题,所以基本不用。垃圾回收中检测垃圾对象主要还是“可达性分析”法。
可达性分析算法:
通过一系列名为“GC Root”的对象作为起点,从这些节点向下搜索,搜索所走过的路径称为引用链(Reference Chain),
当一个对象到GC Root没有任何引用链相连时,则该对象不可达,该对象是不可使用的,垃圾收集器将回收其所占的内存。
所以JVM判断对象需要存活的原则是:能够被一个根对象到达的对象。
可达:对象A引用了对象B,即A到B可达。
GC Root对象集合:
a:Java虚拟机栈(栈帧中的本地变量表)中的引用对象。(当前栈帧的对象引用)
b:方法区中的类静态属性引用的对象。(static对象引用)
c:方法区中的常量引用的对象。(final对象引用)
d:本地方法栈中JNI本地方法的引用对象。
除了堆之外,方法区中的“废弃常量”和“无用的类”需要回收以保证永久代不会发生内存溢出,检测方法区垃圾对象的方法:
1、判断废弃常量的方法(不再需要的常量):如果常量池中的某个常量没有被任何引用所引用,则该常量是废弃常量
2、判断无用的类(不再需要的class文件):
1)该类的所有实例都已经被回收,即Java堆中不存在该类的实例对象
2)加载该类的类加载器已经被回收
3)该类所对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射机制访问该类的方法
当持久代(方法区)满时,将触发Full GC,根据以上标准清除废弃的常量和无用的类。
2.释放空间
垃圾回收算法
1)标记-清除(Mark-Sweep):最基础的垃圾回收算法
标记-清除算法分为两个阶段:标记阶段和清除阶段。
标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。
标记-清除算法容易实现,但问题是,容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。
2)复制(Copying):
将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。
复制(Copying)算法实现简单,运行高效且不容易产生内存碎片,但是能够使用的内存缩减到原来的一半。
Copying算法的效率跟存活对象数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将大大降低。
3)标记-整理(Mark-Compact):
该算法标记阶段和标记-清除(Mark-Sweep)一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。
4)分代(Generation Collection),借助前面三种算法实现:
分代收集算法是目前大部分JVM的垃圾收集器采用的算法。
核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下,将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),
老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。
目前大部分垃圾收集器对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数比较少,但是实际中并不是按照1:1的比例来划分新生代的空间,
一般来说是将新生代划分为一块较大的Eden区和两块较小的Survivor区,每次使用Eden区和其中一块Survivor区,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor区中,
然后清理掉Eden和刚才使用过的Survivor区。
而由于老年代的特点是每次回收都只回收少量对象,一般使用的是Mark-Compact算法。
二、典型的垃圾收集器
HotSpot(JDK 7)虚拟机提供的几种垃圾收集器,用户可以根据自己的需求组合出各个年代使用的收集器。
1、Serial/Serial Old(串行收集器)
Serial/Serial Old收集器是最基本最古老的收集器,它是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程。
Serial收集器是针对新生代的收集器,采用的是Copying算法,Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法。
优点是实现简单高效,缺点是会给用户带来停顿。
2、ParNew
ParNew收集器是Serial收集器的多线程版本,使用多个线程进行垃圾收集。
3、Parallel Scavenge
Parallel Scavenge收集器是一个新生代的多线程收集器(并行收集器),它在回收期间不需要暂停其他用户线程,其采用的是Copying复制算法,
该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量。
4、Parallel Old
Parallel Old是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact(标记-整理)算法。
5、CMS
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep(标记-清除)算法。
(并行收集器:多条垃圾收集线程并行工作,而用户线程仍处于等待状态)
(并发收集器:垃圾收集线程与用户线程一段时间内同时工作(不是并行,而是交替执行))
6、G1
G1收集器是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。
G1收集器是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。
JVM垃圾回收机制与算法
标签:
原文地址:http://blog.csdn.net/ymrfzr/article/details/51354380
踩
(
0
)
赞
(
0
)
举报
评论
一句话评论(
0
)
登录后才能评论!
分享档案
更多>
2021年07月29日 (22)
2021年07月28日 (40)
2021年07月27日 (32)
2021年07月26日 (79)
2021年07月23日 (29)
2021年07月22日 (30)
2021年07月21日 (42)
2021年07月20日 (16)
2021年07月19日 (90)
2021年07月16日 (35)
周排行
更多
Spring Cloud 从入门到精通(一)Nacos 服务中心初探
2021-07-29
基础的排序算法
2021-07-29
SpringBoot|常用配置介绍
2021-07-29
关于 .NET 与 JAVA 在 JIT 编译上的一些差异
2021-07-29
C语言常用函数-toupper()将字符转换为大写英文字母函数
2021-07-29
《手把手教你》系列技巧篇(十)-java+ selenium自动化测试-元素定位大法之By class name(详细教程)
2021-07-28
4-1 YAML配置文件 注入 JavaBean中
2021-07-28
【python】 用来将对象持久化的 pickle 模块
2021-07-28
马拉车算法
2021-07-28
用Python进行冒泡排序
2021-07-28
友情链接
兰亭集智
国之画
百度统计
站长统计
阿里云
chrome插件
新版天听网
关于我们
-
联系我们
-
留言反馈
© 2014
mamicode.com
版权所有 联系我们:gaon5@hotmail.com
迷上了代码!