Java中的GC操作及相关概念
一、GC Roots Tracing的基本思路:通过一系列名为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所经过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链(用图论来说就是GC Roots到这些对象不可达)时,证明这些对象已经不可用
二、Java中,可以作为GC Roots的对象包括以下几种
1 虚拟机栈(桢栈中的本地变量表)中的引用的对象
2 方法区中的类静态属性引用的对象
3 方法区中的常量引用的对象
4 本地方法栈中JNI(即一般说的Native方法)的引用对象
三、Java对象的4种引用类型
1 强引用: 如"Object object=new Object();", 那object就是一个强引用了,只要强引用还存在,GC就不会回收被引用的对象
2 软引用: 用来描述一些还有用,但并非必需的对象
注:对于软引用关联的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围并进行二次回收操作,如果这次回收还是没有足够的内存才会抛出内存溢出常,
Jdk提供了SoftReference来实现软引用
3 弱引用:也是用来描述非必需的对象,但它的强度比弱引用更弱一些,被弱引用关联的对象只能存活到下一次GC回收操作之前,
Jdk提供了WeakReference来实现弱引用
4 虚引用(幽灵引用或幻引用):一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例,为一个对象设置虚引用关联的唯一目的是希望当该对象被GC回收时收到一个系统通知
Jdk提供了PhantomReference来实现虚引用
四、宣告对象死亡至少需要经历两次标记,如果对象在进行根搜索后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并进行一次筛选,筛选的条件是该对象是否有必要执行finalize()方法,当对象没有覆盖finalize()方法或finalize()方法已经被虚拟机执行过,虚拟机将这两种情况都视作“没必要执行”;如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个名为F-Queue的队列中,并在稍后由一条虚拟机自动建立的,低优先级的Finalizer线程去执行,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己--只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋给某个类变量或对象的成员变量,那在第二次标记时它将会被移出“即将回收”的集合
五、一个类成为“无用类”需要满足的三个条件
1 该类的所有实例都已经被回收,也就是说Java堆中不存在该类的任何实例
2 加载该类的ClassLoader已经被回收
3 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法
六、垃圾收集算法
1 标记--清除算法:该算法分为“标记”和“清除”两个阶段,首先标记出需要回收的对象,在标记完成后收掉所有被标记的对象,是最基础的算法,但存在以下两个缺点
(1)效率问题:标记和清除过程的效率都不高
(2)空间问题: 标记清除后会产生大量不连续的内存碎片,碎片太多导致当需要申请连续的大片空间的时候如果没有可用空间需要再触发一次垃圾收集动作
2 复制算法: 它将可用内存按容量大小分为相等的两块,每次只使用其中的一块,当这一块的内存用完了就将还存活的对象复制到另一块上去,然后再把已使用过的那一半内存空间一次性清理掉
优点:每次只是对其中的一块进行内存回收,内存分配时不用考虑内存碎片等复杂问题,只要移动堆顶指针按顺序分配内存即可,实现简单、运行高效
缺点:将内存缩小了一半,代价太高
3 标记整理算法: 标记过程仍然与“标记--清除算法”一样,但后续步骤不是对可回收对象进行清理,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存
4 分代收集算法: 根据对象存活周期不同将内存分为几块,一般是把Java堆分为新生代和老年代,不同的区采用不同的垃圾收集算法,新生代中由于大量对象死去,只有少量存活,一般选用复制算法,老年代中因为对象存活率高没有额外空间进行分配担保,就必须使用“标记--清除”或“标记--整理”算法
七、垃圾收集器
1 Serial收集器:这个收集器是一个单线程的收集器,它的“单线程”的意义不仅仅说明它只有一个CPU或只有一条收集线程去进行垃圾收集工作,更重要的时在进行垃圾收集时,必须暂停其它所有的工作线程,直到垃圾收集结束
2 ParNew收集器:该收集器是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集外,其它的包括Serial可用的控制参数,收集算法等基本上一样
3 Parallel Scavenge收集器(吞吐量优先收集器): 它是一个新生代收集器,使用复制算法且是并行的多线程收集器
(1)Parallel Scavenge的特点是它的关注点与其它收集器不同,CMS(Concurrent Mark Sweep)等收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标是达到一个可控的吞吐目量(Throughput)
(2)吞吐量是CPU用于运行用户代码的时间与CPU总消耗时间的比例,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
(3)高吞吐量可以最高效地利用CPU时间,主要适用在后台运算而不需要太多用户交互的任务
(4)Parallel Scavenge用于精确控制吞吐量的两个重要参数:-XX:MaxGCPauseMillis(控制最大垃圾收集停顿时间)和-XX:GCTimeRatio(设置吞吐量大小)
4 Serial Old收集器: Serial收集器的老年代版本,它同样是单线程收集器,使用“标记--整理”算法
5 Parallel Old收集器:它是Parallel Scavenge收集器的老年代版本,使用多线程和“标记--整理”算法
6 CMS(Concurrent Mark Sweep)收集器: 它是一种以获取最短回收停顿时间为目标的收集器,尤其适用于Java应用服务器或B/S服务器,它基于“标记--清除”算法来实现
(1)CMS收集器的运作过程包括以下4个步骤:
A 初始标记(CMS initial mark): 仅仅标记一下GC Roots能直接关联到的对象,速度很快
B 并发标记(CMS concurrent mark): 进行GC Roots Tracing的过程
C 重新标记(CMS remark): 修正并发标记期间因用户程序继续运作而导致标记变动的那一部分对象的标记记录,这个阶段停顿时间一般比初始标记阶段长一些,但远比并发标记阶段短
D 并发清除(CMS concurrent sweep)
(2)初始标记和重新标记两个步骤仍然需要停止用户线程
(3)整个过程中耗时最长的并发标记和并发清除过程中,收集器线程可以与用户线程一起工作,所以总体上来讲CMS收集器的回收过程是与用户线程一起并发执行的
(4)CMS收集器的三个显著缺点
A CMS收集器对CPU资源非常敏感
B CMS收集器无法处理浮动垃圾(Floating Garbage),可能出现"Concurrent Mode Failure"失败而导致另一次Full GC操作,另外,由于垃圾收集阶段用户线程还需要继续运行,即还需要预留足够的内存空间给用户线程使用,因此CMS无法等到老年代几乎完全被填满再进行垃圾收集,需要预留一部分空间供并发收集时的用户线程使用,要是CMS运行期间预留的内存无法满足程序需要就会出现一次“Concurrent Mode Failure”失败
C CMS收集结束时会产生大量的内存碎片,因为CMS是基于“标记--清除”算法的
7 G1收集器(Garbage First): 它是当前收集器发展的最前沿成果
(1)G1收集器与CMS收集器相比有两个显著的改进
A G1收集器是基于“标记--整理”的算法实现的收集器,它不会产生空间碎片
B 它可以精确地控制停顿,即能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不会超过N毫秒
(2)G1收集器可以在基本不牺牲吞吐量的前提下完成低停顿的内存回收,这是由于它能极力地避免全区域垃圾收集,之前的垃圾收集器进行收集的范围都是整个新生代或老年代,而G1将整个Java堆(包括新生代和老年代)划分为多个大小固定的独立区域(Region),并且跟踪这些区域里的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先收集垃圾最多的区域
八、HotSpot JVM1.6的垃圾收集器
说明:如果两个垃圾收集器之间有线相连,说明这两款垃圾收集器可以搭配使用
九、内存分配的5条基本原则
1 对象优先在Eden区分配: 大多数情况下,对象在新生代的Eden区中分配,当Eden区没有足够的空间进行分配时会发生一次Minor GC操作
2 大对象直接进入老年代: 大对象是指需要大量连续内存空间的Java对象,虚拟机提供了一个-XX:MaxTenuringThreshold参数令大于这个设置值的对象直接在老年代中分配空间,这样做的目的是避免在Eden区及两个Survivor区之间大量的对象拷贝
注:-XX:MaxTenuringThreshold只对Serial和ParNew两款收集器有用
3 长期存活的对象将直接进入老年代: 虚拟机给每个对象定义了一个对象年龄(Age)计数器,如果对象在Eden区出生并经过第一次Minor GC后仍然存活并且能被Survivor容纳的话,将被移到到Survivor区并将对象年龄设为1,对象在Survior每熬过一次Minor GC年龄就加1,当它的年龄达到-XX:MaxTenuringThreshold的设置值后就会被晋升到老年代中
4 动态对象年龄判断: 为了更好地适应不同程序的内存状况,虚拟机并不总是要求对象年龄达到-XX:MaxTenuringThreshold设置值才能晋升老年代,如果在Survivor中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年区,无须等到年龄达到-XX:MaxTenuringThreshold设定的值
5 空间分配担保: 在发生Minor GC时,虚拟机会检测之前晋升到老年代的平均大小是否大于老年代的剩余空间大小,如果大于则直接进行一次Full GC操作,如果小于则看HandlePromotionFailure设置是否允许担保失败,如果允许就进行一次Minor GC,否则进行一次Full GC,当出现大量对象在Minor GC后仍然存活的情况时(最极端的情况是内存回收后新生代中所有对象都存活)就需要老年代进行分配担保,让Survivor无法容纳的对象直接进入老年代
Java中的GC操作及相关概念的更多相关文章
- JAVA中的时间操作
java中的时间操作不外乎这四种情况: 1.获取当前时间 2.获取某个时间的某种格式 3.设置时间 4.时间的运算 好,下面就针对这四种情况,一个一个搞定. 一.获取当前时间 有两种方式可以获得,第一 ...
- java中的集合操作类(未完待续)
申明: 实习生的肤浅理解,如发现有错误之处.还望大牛们多多指点 废话 事实上我写java的后台操作,我每次都会遇到一条语句:List<XXXXX> list = new ArrayList ...
- JAVA中通过Jedis操作Redis连接与插入简单库
一.简述 JAVA中通过Jedis操作Redis连接与插入简单库 二.依赖 <!-- https://mvnrepository.com/artifact/redis.clients/jedis ...
- Java中的文件操作(一)RandomAccessFile
今天,学到的是java中的文件操作. Java.IO.File Java中操作文件用到RandomAccessFile类,既可以读取文件内容,也可以向文件输出数据,但不同与普通输入/输出流的是Rand ...
- Java并发--Java中的CAS操作和实现原理
版权声明:本文为博主原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/CringKong/article/deta ...
- Java中的字符串操作(比较String,StringBuiler和StringBuffer)
一.前言 刚开始学习Java时,作为只会C语言的小白,就为其中的字符串操作而感到震撼.相比之下,C语言在字节数组中保存一个结尾的\0去表示字符串,想实现字符串拼接,还需要调用strcpy库函数或者自己 ...
- Java中的IO操作和缓冲区
目录 Java中的IO操作和缓冲区 一.简述 二.IO流的介绍 什么是流 输入输出流的作用范围 三.Java中的字节流和字符流 字节流 字符流 二者的联系 1.InputStreamReader 2. ...
- JAVA中的GC机制详解
优秀Java程序员必须了解的GC工作原理 一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只 ...
- java中的IO操作
IO流是用来处理设备之间的数据传输,Java对数据的操作是通过流的方式进行,而操作流的对象都封装到java.io包中.根据操作数据的种类可以把IO流分为字节流(InputStream,OutputSt ...
随机推荐
- QQ登入(3)QQ空间分享-无需登入
public void myclick2(View v){ //QZone分享, SHARE_TO_QQ_TYPE_DEFAULT 图文,SHARE_TO_QQ_TYPE_IMAGE 纯图 ,SHAR ...
- ajax方法完整的事件流
- Hadoop 源码编译导出
https://svn.apache.org/repos/asf/hadoop/common/branches/branch-trunk-win/BUILDING.txt http://www.src ...
- FastJson的基本用法----转
1. [文件] User.java ~ 243B 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class User{ private int id; ...
- Android 隐藏Fragment
1.隐藏Fragment FragmentManager fManager = getFragmentManager(); fManager.beginTransaction() .setCustom ...
- web前端基础知识及快速入门指南
web前端基础知识及快速入门指南 做前端开发有几个月了,虽然说是几个月,但是中间断断续续的上课.考试以及其它杂七杂八的事情,到现在居然一直感觉自己虽然很多前端的知识很眼熟,却也感觉自己貌似也知识在门口 ...
- sql访注入
http://www.dewen.org/q/6154/java%E7%A8%8B%E5%BA%8F%E9%98%B2%E6%AD%A2sql%E6%B3%A8%E5%85%A5%E7%9A%84%E ...
- 批处理安装APK
set adbpa=D:\Nexus6\ota51\ADBTool set apkpa=D:\WDJDownload\Apps %adbpa%\adb.exe install %apkpa%\CPU- ...
- Android开发——通过扫描二维码,打开或者下载Android应用
Android开发——通过扫描二维码,打开或者下载Android应用 在实现这个功能的时候,被不同的浏览器折磨的胃疼,最后实现了勉强能用,也查考了一下其他人的博客 android实现通过浏览器点击 ...
- 使用Maven Profile实现多环境构建
在开发过程中,我们的软件会面对不同的运行环境,比如开发环境.测试环境.生产环境,而我们的软件在不同的环境中,有的配置可能会不一样,比如数据源配置.日志文件配置.以及一些软件运行过程中的基本配置,那每次 ...