美文网首页
Java垃圾回收机制学习笔记

Java垃圾回收机制学习笔记

作者: asdf1st | 来源:发表于2020-04-24 20:13 被阅读0次

    Java运行时数据区域

    在这之前先了解JVM运行时的数据区域,分五块:

    程序计数器

    用于表示当前代码执行到第几行

    虚拟机栈

    虚拟机栈描述的是 Java 方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame,是方法运行时的基础数据结构)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

    本地方法栈

    本地方法栈与虚拟机栈所发挥的作用是非常相似,虚拟机栈对应描述的是Java方法,而本地方法栈则描述的是Native(C++)方法。

    方法区

    方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。
    JDK8 之前,Hotspot(虚拟机) 中方法区的实现是永久代(Perm),JDK8 开始使用元空间(Metaspace),以前永久代所有内容的字符串常量移至堆内存,其他内容移至元空间,元空间直接在本地内存分配。

    对于大多数应用来说,Java 堆是 Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”。从内存回收的角度来看,由于现在收集器基本都采用分代收集算法,所以 Java 堆中还可以细分为:新生代和老年代;再细致一点的有 Eden 空间、From Survivor 空间、To Survivor 空间。

    很明显Java垃圾回收主要作用于区域,而区域又分为新生代老年代,而网上说的持久代(永久代)其实是存于方法区中,并且于JDK1.8后以转移到元空间中(以前永久代所有内容的字符串常量移至堆内存,其他内容移至元空间,元空间直接在本地内存分配)

    再介绍下堆里又划分2块区域,分别是新生代和老年代(对应比例1:2)

    新生代

    主要存放新生成的对象,新生代的对象一般是朝生夕灭(存活期短),而新生代的区域又划分为三个区域,分别叫Eden, From Survivor,To Survivor(比例对应为8:1:1)
    在新生代区域触发的GC叫做Minor GC

    老年代

    当新生代的对象经过一定次数的回收仍存活(这个次数为一个规定的阈值),那么则会由新生代移动到老年代.老年代则会触发Full GC(同时清除新生代,老年代,永久代)

    如何判断对象是否为垃圾

    1.引用计数法

    给对象加上计数器,引用+1,失效-1。当引用数为0则为内存垃圾

    但是引用计数法存在一个漏洞,那就是无法解决循环引用的bug(A持有B的引用,B持有A的引用,运用这个方法则导致两者都无法被回收)因此出现第二种方法

    2.可达性分析法

    通过GC Roots作为起始点,从这些节点开始向下搜索,搜索走过的路径叫做引用链。当一个对象与任何一个GC Roots没有引用链相连时,就认为该对象是内存垃圾。
    同样以上面的案例分析(A,B虽然相互持有引用,但没有被任何一个GC Roots引用,因此仍然被认为是内存垃圾)
    而到底什么对象可以作为GC Roots呢,以下对象都可以 :

    • 虚拟机栈(栈帧中的本地变量表)中引用的对象。
    • 方法区中类静态属性引用的对象。
    • 方法区中常量引用的对象。
    • 本地方法栈中JNI(即一般说的Native方法)引用的对象。

    Java垃圾回收算法

    知道如何判定对象是否为垃圾后就可以制定垃圾回收的策略(算法)了,分为4种

    1.标记-清除算法

    标记-清除算法采用从根集合(GC Roots)进行扫描,对存活的对象进行标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收
    优点: 并没有移动仍存活对象的内存,对于正在运行的应用进程影响较少
    缺点:1 每个对象找一遍导致效率不高
            2 容易产生大量不连续内存碎片无法利用

    2.复制算法

    把内存分成两半,对象分配内存时只分配到其中一半的区域,当这一半区域内存满了则把这部分内存整理复制到另一半区域,同时把另一半的区域的内存清空
    优点:简单高效(相对于上面的方法只需遍历内存的一半),不会出现内存碎片
    缺点:一半的内存空间无法使用,代价太大

    3.标记-整理算法

    先标记(标记方法跟第一种方法一样),然后把存活的对象移向一端,然后再把其余区域的内存清掉
    优点:1 相比标记-清除算法不会产生内存碎片
            2.相比复制算法不会产生空间浪费

    4.分代收集算法

    其实严格来说分代收集算法只是前面3种算法在Java虚拟机中实际的应用:对于新生代内存,采用复制算法,对于老年代内存,采用标记-整理算法

    Dalvik与Art虚拟机在垃圾回收算法上的区别

    Dalvik:只能固定一种垃圾回收算法,出厂就固定了
    Art:能够根据实际情况选择垃圾回收算法(例如对于正在前台运行的app采用标记-清除算法,对于转到后台运行的app则可以采用标记-整理算法)

    触发GC的时机

    Minor GC触发机制:

    当年轻代满时就会触发Minor GC,这里的年轻代满指的是Eden代满,Survivor满不会引发GC。

    Full GC触发机制:

    (1)调用System.gc时,系统建议执行Full GC,但是不必然执行
    (2)老年代空间不足
    (3)方法区空间不足
    (4)通过Minor GC后进入老年代的大小大于老年代的可用内存
    (5)由Eden区、survivor space1(From Space)区向survivor space2(To Space)区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

    堆的垃圾回收工作流程

    新生成的对象会在Eden区中存在,当Eden内存已满把Eden无效的数据清除,存活的数据(没能释放内存的数据)移到From Survivor,假如From Survivor内存满了则把Eden 和From Survivor的内存整理移到To Survivor中,如此反复(假如其中一个Survivor并不足以承载Eden+另一个Survivor的内存时直接转移到老年代区域),在Survivor中只要一次GC没把对象内存清掉给内存计数就会+1,当这个计数达到一定阈值会从新生代区域转移到老年代区域,当老年代区域内存已满或出现其他情况又会触发的的Full GC

    相关文章

      网友评论

          本文标题:Java垃圾回收机制学习笔记

          本文链接:https://www.haomeiwen.com/subject/hwofwhtx.html