概述
1.概述
垃圾收集(Garbage Collection,GC)其实年代比java久远。
GC需要考虑的是三件事:哪些内存需要回收、什么时候回收、如何回收。
而我们主要做的虚拟机性能调优,主要处理的就是方法区和堆的GC回收优化,其它内存区域基本不需要。
2.对象是否已死
这里是第一件事:哪些内存需要回收->对象已死的内存需要回收。
2.1 引用计数算法
引用计数算法是这么做的:给对象添加一个引用计数器,每当有一个地方引用这对象时,计数器值+1,引用失效时,计数器值-1;任何时刻当计数器值为0时,说明这个对象不可能再被使用,判定为对象已死。
缺点:相互引用无法回收,例:A、B对象,A引用B,B引用A,那么相互引用,但实际不会再被访问,所以还是不发通知GC回收。
2.2 可达性分析算法
这个是目前主流的实现(java、C#),都是通过可达性分析来判断对象是否存活的。
可达性分析算法的思路:以被称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索走过的路径称为引用链(Referrence Chain),当一个对象到GC Roots没有任何引用链(就是两者之间不可达)时,证明对象已死。
可作为GC Roots的对象包括:
1.虚拟机栈(栈帧本地变量表)中引用的对象
2.方法区中类静态属性引用的对象
3.方法区中常量引用的对象
4.本地方法栈中JNI(就是一般的Native方法)引用的对象。
2.3 再谈引用
JDK1.2之后,java引用概念由引用强度的依次减弱,分为四种引用:强引用、软引用、弱引用、虚引用。
强引用:程序代码中普遍存在的,例:String a=new String();只要强引用还在,GC永远不会回收被引用的对象
软引用:用来描述一些有用但不是必需的对象,它会在将要发生内存溢出前进行第二次回收,第二次回收还是没有足够内存,则抛异常。JDK使用SoftReference类来实现软引用。
弱引用:描述非必需对象,对象将在下次GC之前被回收,JDK使用WeakReference类来实现
虚引用:又称幽灵引用,虚引用完全不会对其生命周期构成影响,也无法通过虚引用取得一个对象实例。作用只是在GC回收时能收到一条系统通知。JDK使用PhantomReference类来实现虚引用。
2.4 二次判断对象死亡
实际上java的判断对象死亡会经历两次
第一次就是之前讲的可达性分析算法,第二次就是判断该对象是否有必要执行finalize()方法
finalize()方法相当于死缓,或者说遗愿清单,可以将最后要回收释放的东西处理完毕后再GC回收。
有些人将finalize()用于关闭外部资源,不过实际上,并不推荐使用finalize()方法。
2.5 回收方法区
方法区不像前面的对象回收一样大量地进行回收,不过回收方法区也有一部分的东西需要回收。
这里方法区要回收的分为两类:一类就是常量池里的废弃常量、一类就是无用的类。
废弃常量:直接根据是否引用即可判断是否需要回收。
无用的类:需要满足三个条件,才可以回收(注意是可以,不是必然回收)
条件1:该类的对象实例都已被回收
条件2:加载该类的ClassLoader已被回收
条件3:该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方反射访问该类的方法。
3.垃圾收集算法(GC算法)
这里主要讲多种算法的思想。
3.1 标记-清除算法
该算法分“标记”、“清除”两阶段:标出所有要回收的对象,然后统一回收。
缺点:效率不高(标记和清除效率都不高)、空间问题(标记清除会产生大量不连续内存碎片,碎片太多分配大内存对象又得GC)。
3.2 复制算法
该算法将可用内存划分为大小相等的两块,每次只使用其中的一块,一块用完了,还存活的对象就复制到另一块上面,然后再把已使用的内存空间一次清理掉。也就是用一半,清一半。
缺点:将内存缩小为了原来的一半。
PS:现代商业虚拟机是这样类似的算法,不过不是分成大小相等的两块,而是一大(Eden)两小(Survivor)的三块,每次用一大一小,然后还存活的给另外一小,再清理。Hotspot虚拟机默认比例:Eden:Survivor=8:1。也就是每次用90%的内存空间
3.3 标记-整理算法
这个算法标记过程和标记-清除算法一样,不过它标记后不是直接清除
而是将所有存活对象移向一端,清理掉端边界外的无用内存。
3.4 分代收集算法
这也是现代商业虚拟机一起采用的一种算法,就是根据对象存活周期的不同划分为几块。
一般把java堆划分为新生代和老年代,
新生代放时常对象死亡的,用复制算法(当存活放到另一块内存,内存不够时,放到老年代去)
老年代存活率高用标记-xx算法。
4.HotSpot的算法实现
上面讲了对象是否已死的算法和垃圾收集的算法,而这一节将虚拟机是如何实现这些算法的。
4.1 枚举根节点
目前主流的java虚拟机使用准确式GC,也就是说不检查全局的引用位置和执行上下文,有办法得知哪些地方存放对象引用。
HotSpot实现中通过一组OopMap的数据结构来达到这个目的,类加载完时,HotSpot就把对象内什么类型的数据子算出来,JIT编译时,就在特定位置记录栈和寄存器哪些位置是引用。
PS:可达性分析,GC回收都是需要停顿所有java执行线程的,所以HotSpot记录时也是需要停顿的
4.2 安全点
什么是安全点?
在OopMap下,HotSpot可以快速完成GC Roots枚举,但由于指令繁多,所以HotSpot实质上不是为每条指令都生成OopMap的
而其解决方法是在特定的位置记录这些新兴,而这些位置就是安全点,也就是说到安全点停顿然后记录。
GC发生时,如何让所有线程跑到最近的安全点,再停顿?
这里有两种方案:抢先式中断和主动式中断。
抢先式中断:不需要线程代码主动配合,GC发生时,先中断全部线程,如一些线程不在安全点,就恢复线程,让它跑到安全点。(不过这种方式,现在基本不用)
主动式中断:GC发生时,不直接中断线程,而是设置一个标志,各线程执行时主动去轮询该标志,标志为true,则自己中断挂起(PS:轮询标志的地方应该和安全点重合)
4.3 安全区域
安全区域是为了程序不执行时(没有分配CPU时间)而设置的,因为这时线程是无法响应JVM中断请求的。
安全区域是指一段代码片段之中引用关系不会发生 变化,在这个区域中的任意地方开始GC都是安全的。
最后
以上就是甜美小松鼠为你收集整理的深入理解jvm学习笔记02:自动内存管理机制之GC与内存分配策略(上)1.概述2.对象是否已死3.垃圾收集算法(GC算法)4.HotSpot的算法实现的全部内容,希望文章能够帮你解决深入理解jvm学习笔记02:自动内存管理机制之GC与内存分配策略(上)1.概述2.对象是否已死3.垃圾收集算法(GC算法)4.HotSpot的算法实现所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复