Java基础:GC机制
上一节,简单的介绍了java当中的内存模型,那么经常和内存模型一起提到的JAVA垃圾回收机制当然也需要在这里一并的总结一下。
所谓是垃圾回收机制,用通俗的话来说,就是将那些没有被任何变量引用的实例对象自动的进行垃圾回收,重新释放出可用的内存,以供之后使用的方法。在java当中垃圾回收机制几乎是程序员不可编程的,但是也正因为程序员不可编程,也可以说是不需要编程,所以java相比C++来在这一点来说,人性化的很多,因为C++当中需要在实例化一个对象之后,当这个对象不再被需要的时候,手动调用析构函数对无用实例进行回收,而java当中有一个完整的垃圾回收机制对这些对象进行回收,编程人员无需关注这些不再被引用的实例何时回收,对于编程人员来说,可以把精力更加的注重于逻辑上的业务开发。
那么问题来了,java当中,如何实现对不再被引用(有的说法叫不被其他任何对象持有)的实例进行回收的呢?接下来才是我们要介绍的重头戏。
首先我们要先介绍几种常规的垃圾回收机制的算法(非java使用的垃圾回收机制的算法):
1.引用计数算法
该算法的大概思路就是每一个实例对象都计算指向它的指针数量(在java当中是引用变量指向它的数量),当有一个指针(或者引用变量)指向自己的时候,计数值+1,反之,当指向自己的指针删除(或引用变量不再指向自己的时候)计数值-1,当计数值为0的时候,说明该对象已经不存在指向的指针(或者指向它的引用变量,它不再被持有),所以就可以把它安全的回收。
算法优点:算法简单,并且回收没有延迟。
算法缺点:1)需要存储计数器,增加了存储空间的开销、2)需要更新计数器,增加了时间开销、3)无法处理环形引用的问题。
2.标记-清除算法
该算法的主要思路是对于所有或者的实例对象,进行一次全局的遍历来确定哪些实例是可以被回收的、哪些实例是还被持有的、遍历的过程如下:从根出发,找到所有可达的实例,对这些可达的实例进行标记,然后剩下的没有被标记的实例,则是可以被清除的,然后将这些实例进行回收。
算法优点:1)很自然的解决了环形引用的问题。2)不需要提供额外的计数器的开销。
算法缺点:1)该算法在垃圾回收器运行的过程中、应用程序(stop-to-world)必须停止。2)需要遍历所有的存活对象,会造成一定的开销。3)在清除阶段,清除垃圾对象后会造成大量的内存碎片(你想问为什么会造成碎片?其实很简单,看下图就知道了。)
3.标记-清除-复制
为了解决上述的内部碎片化的问题,是对于上边的一种方法的改进,基本的标记算法和上述大致相同,不一样的地方在于这种算法会把内存分为了A、B两个区域,当内存回收的时候,将A中的内存块被标记的实例拷贝到B中(这时候拷贝过去就对对象所存的位置进行了整理,使它不再碎片),然后一次性清空A。
算法优点:解决了碎片化的问题
算法缺点:对内存的大小要求为原来的两倍。
上述介绍的图片部分来源于:http://blog.csdn.net/qq_26437925/article/details/53728388
4.标记-清除-压缩
除了对内存进行分区,然后交替使用的算法外,还有一种标记-清除-压缩算法,标记部分算法和上述一样,但是不同的是,在进行清除之前,不再进行分区,而是将对象都就是移动所有的可达对象到堆内存的同一个区域中,使他们紧凑的排列在一起,从而将所有非可达对象释放出来的空闲内存都集中在一起,通过这样的方式来达到减少内存碎片的目的。
注意,该算法是一个需要暂停应用程序的算法(stop-to-world)
介绍完这几种典型的垃圾回收算法之后,你可能会问,说了一大堆,你还没有告诉我,java的垃圾回收机制是什么样的呢?错!java的回收机制,就是相当于集了以上几种算法的大成之作!
5.java的垃圾回收机制
java当中的垃圾回收机制(GC机制)主要面向的对象是内存当中的堆,还有方法区,并且在java当中,采用了分代回收的算法,那么都分为了什么代呢?
1)年轻代
主要是用来装新生成的对象的,而年轻代设立出来的主要目的,就是为了尽可能快速的回收那些声明周期短的对象。而在年轻代当中,一般又被分为了三个区,一个名叫Eden区,而剩下两个则都为Survivor区(姑且称为SA区和SB区吧,这两个区的地位是完全相同的,当然Survivor区也可以分为多个)。大部分的实例都是在这个Eden区当中生成(一些很大的实例,直接就在老年代当中),当Eden区被占满后,将会触发垃圾回收,面向年轻代的垃圾回收被称之为Minor GC,这时候会通过标记-清除-复制算法,将Eden区当中还被持有的实例进行标记后,整理复制到Survivor区(A或者B都有可能),并且将Eden区当中的所有的实例都回收掉,而这时候被复制到Survivor区的则是这次GC的幸存者。而当Survivor区也被占满时(注意,SA区和B区,在同一时间,一定是一个区被用,则另一个区为空的,这里姑且假设被用的A区)就会又触发Minor GC,这时候还是采用标记-清楚-复制算法,将A区的被标记的实例再整理复制到B区当中,并且将A区清空。而当B区又被占满,这时候还是同样再将幸存下来的实例在复制到A去当中,但是有一点不同的是,如果再A区和B区的GC当中都幸存下来的实例,则将会复制到老年代当中,而不再存在于年轻代。
2)老年代
在年轻代中经历了N次(在上述例子当中,应该是3次Minor GC)垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为老年代是存着一些生命周期较长的对象,而当老年代也被占满的时候,则会触发Major GC(面向老年代的GC被称为Major GC,速度比minorGC要慢),一般采用的是标记--清除-压缩算法。
3)永久代
也就是在内存机制当中经常说到的方法区,一般不会被回收。
注意:从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC,对老年代GC称为Major GC,而Full GC是对整个堆来说的,在最近几个版本的JDK里默认包括了对永生带即方法区的回收(JDK8中无永生带了),出现Full GC的时候经常伴随至少一次的Minor GC,但非绝对的。Major GC的速度一般会比Minor GC慢10倍以上。下边看看有那种情况触发JVM进行Full GC及应对策略。
注意:在MinorGC当中使用的标记-清除-复制算法也是需要stop-the-world的,但是对于年轻带来说,需要标记的对象不多,所以时间可以忽略不计。而且在MajorGC当中,由于需要标记大部分的对象,所以这个时间很长。导致了MajorGC比MinorGC时间要长的多的原因之一,而另一个原因是MajorGC当中由于还需要压缩,所以也相当耗时。
接下来的内存,参考了博客:http://www.importnew.com/15330.html
接下来我们再详细的看看垃圾回收算法有哪几种引用的形式?
1)串行回收(只用一个CPU)和并行回收(多个CPU才有用):串行回收是不管系统有多少个CPU,始终只用一个CPU来执行垃圾回收操作,而并行回收就是把整个回收工作拆分成多个部分,每个部分由一个CPU负责,从而让多个CPU并行回收。并行回收的执行效率很高,但复杂度增加,另外也有一些副作用,如内存碎片增加。
2)并发执行和应用程序停止 :应用程序停止(Stop-the-world)顾名思义,其垃圾回收方式在执行垃圾回收的同时会导致应用程序的暂停。并发执行的垃圾回收虽然不会导致应用程序的暂停,但由于并发执行垃圾需要解决和应用程序的执行冲突(应用程序可能在垃圾回收的过程修改对象),因此并发执行垃圾回收的系统开销比Stop-the-world高,而且执行时需要更多的堆内存。
(后边都是一些垃圾回收器的介绍,表示看不懂了。。。。。。)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
这里更正一下,只有对象不被引用的时候,垃圾回收机制才会回收该对象,这句话是不严谨的,因为环型持有的情况下,也会对循环持有的对象进行回收。比如A持有B,B持有C,C持有A,这时候A/B/C可能都会被回收,是如何回收的呢?垃圾回收机制除了检查对象是否被引用外,还要看对象是否被至少一个GC roots对象直接或者间接引用,所以当A和B对象都退出了方法的时候,这时候这两个对象虽然相互为强引用,但是却不被GC roots对象直接或者间接的引用,所以会被回收。
那么问题来了。所谓的GC roots对象又是什么对象呢?换个问题,所谓的从根节点遍历,这个根节点又是什么呢?
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 本地方法栈中JNI(即一般说的Native方法)引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
看到这里,恭喜你,对java当中的垃圾回收的了解就更进一步了,那么我们再来看看,垃圾回收当中的一个重要方法 finalize()方法。
在之前我们只是简单的提到过这个方法,是在对象要被回收之前一般都会执行的一个方法(一般,说明JVM不保证finalize函数一定会被调用,比如在JVM退出的当口,内存中那些对象的finalize函数可能就不会被调用了),在执行了finalize方法之后,只有在下一次垃圾收集过程中,才会真正回收对象的内存。这里需要提到一点,执行finalize方法之后的对象不一定就会被回收,根据 Java 文档,finalize() 是一个用于释放非 Java 资源的方法,换个方式来解释,就是比如你的对象当中,需要用到外部的资源,比如tcp socket、mysql的连接,而当你的对象突然不被使用了,需要被回收了,如果没有finalize方法,可能你的对象只是回收了java部分的资源,而外部的资源却没有被close,所以finalize方法就是确保对象被回收之前,外部资源都会被释放的方法。
Java基础:GC机制的更多相关文章
- Java基础-类加载机制与自定义类Java类加载器(ClassLoader)
Java基础-类加载机制与自定义类Java类加载器(ClassLoader) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 关于类加载器的概念和分类我就不再废话了,因为我在之前的笔 ...
- java基础---GC
一.Java基础: GC即:garbage collection垃圾回收机制.Java是自动回收垃圾的,像c c++等语言没有自动垃圾回收机制,长时间开启服务器就会导致 内存泄漏,占用内存 Java的 ...
- Java 虚拟机 - GC机制
GC机制的一些总结 https://blog.csdn.net/super_qing_/article/details/85263991 https://blog.csdn.net/yhyr_ycy/ ...
- JAVA基础-反射机制
什么是JAVA的反射机制 Java反射是Java被视为动态(或准动态)语言的一个关键性质.这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其 ...
- JVM(3)对象A和B循环引用,最后会不会不被GC回收?-------关于Java的GC机制
①首先说一下,GC里边在JVM其中是使用的ROOT算法,ROOT算法,什么称作为ROOT呢,就是说类的静态成员,静态成员就是static修饰的那种,是"根"的一个,根还包含方法中的 ...
- JAVA 调用gc机制强制删除文件
在删除文件前调用System.gc()方法,也就是垃圾回收机制,即可成功删除被JAVA虚拟机占用的文件.
- java基础——反射机制
反射机制是什么 反射机制就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为jav ...
- Java基础——类加载机制
什么叫类加载 JVM把 .class 字节码文件加载到内存,并进行相关的校验.解析.初始化,最终转换为虚拟机可用的JAVA类型的过程,称为JVM类加载机制. (当然,JVM并不关心class文件的来源 ...
- Java基础-异常处理机制 及异常处理的五个关键字:try/catch/finally/throw /throws
笔记: /** 异常处理机制: 抓抛模型 * 1."抛", 一旦抛出,程序终止! printStackTrace()显示异常路径! * 2."抓", 抓住异常 ...
- Java的GC机制及算法
GC的阶段 对每个对象而言,垃圾回收分为两个阶段:finalization和reclamation. finalization: 指运行这个对象的finalize的方法. reclamation: ...
随机推荐
- 处理大并发量订单处理的 KafKa部署总结
处理大并发量订单处理的 KafKa部署总结 今天要介绍的是消息中间件KafKa,应该说是一个很牛的中间件吧,背靠Apache 与很多有名的中间件搭配起来用效果更好哦 ,为什么不用RabbitMQ,因为 ...
- FFT多项式乘法模板
有时间来补算法原理orz #include <iostream> #include <cstdio> #include <cmath> #include <c ...
- Codeforces Round #392(Div 2) 758F(数论)
题目大意 求从l到r的整数中长度为n的等比数列个数,公比可以为分数 首先n=1的时候,直接输出r-l+1即可 n=2的时候,就是C(n, 2)*2 考虑n>2的情况 不妨设公比为p/q(p和q互 ...
- Word2010 自动生成二级编号
http://jingyan.baidu.com/article/3ea5148901919752e61bbafe.html
- 【C++ 拾遗】Function-like Macros
Macro expansion is done by the C preprocessor at the beginning of compilation. The C preprocessor is ...
- 【题解】NOI2015寿司晚宴
想好久啊+不敢写啊……但果然人还是应当勇敢自信,只有坚定地去尝试,才会知道最后的结果.1A真的太开心啦,不过好像我的做法还是比较复杂的样子……理解起来应该算是比较容易好懂的类型,大家可以参考一下思路~ ...
- BZOJ5011 [JXOI2017]颜色 【线段树 + 主席树】
题目链接 BZOJ5011 题解 一定只有我这种智障会用这么奇怪的方法做这道题.. 由题我们知道最后剩余的一定是一个区间,而且区间内的颜色不存在于区间外 所以我们的目的就是为了找到这样的区间的数量 区 ...
- BZOJ4008 [HNOI2015]亚瑟王 【概率dp】
题目链接 BZOJ4008 题解 要求所有牌造成伤害的期望,就是求每一张牌发动的概率\(g[i]\) 我们发现一张牌能否发动,还与其前面的牌是否发动有关 那我们设\(f[i][j]\)表示前\(i\) ...
- 【BZOJ1458】士兵占领 最大流的模板题
我们只要把他们可以有的限制用流量限制,再用两者关系限制一下就可以开心的跑了. #include <cstdio> #include <cstring> #include < ...
- 如何用setInterval调用类的方法
setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式.setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭.由 se ...