我是靠谱客的博主 甜美小松鼠,最近开发中收集的这篇文章主要介绍深入理解jvm学习笔记02:自动内存管理机制之GC与内存分配策略(上)1.概述2.对象是否已死3.垃圾收集算法(GC算法)4.HotSpot的算法实现,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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的算法实现所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(61)

评论列表共有 0 条评论

立即
投稿
返回
顶部