宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇

宋红康MySQL高级篇 宋红康微博_GCRoots包含哪些对象_02

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_03

宋红康MySQL高级篇 宋红康微博_java垃圾回收_04

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_05

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_06

宋红康MySQL高级篇 宋红康微博_java垃圾回收_07

宋红康MySQL高级篇 宋红康微博_java垃圾回收_08

宋红康MySQL高级篇 宋红康微博_GCRoots包含哪些对象_09

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_10

宋红康MySQL高级篇 宋红康微博_jvm_11

宋红康MySQL高级篇 宋红康微博_GCRoots包含哪些对象_12

宋红康MySQL高级篇 宋红康微博_jvm_13

宋红康MySQL高级篇 宋红康微博_java垃圾回收_14

宋红康MySQL高级篇 宋红康微博_java垃圾回收_15

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_16

宋红康MySQL高级篇 宋红康微博_java垃圾回收_17

【对象本身已经不再使用了,但是由于在虚拟机栈中仍然有一些引用在指向着它,所以它没有办法被垃圾回收器回收,因此就说产生了内存泄漏。如果垃圾对象永远无法清除,那么,随着应用系统运行时间的不断增长,垃圾对象所消耗的总内存会持续上升,直到出现内存溢出,造成应用系统崩溃。】

宋红康MySQL高级篇 宋红康微博_jvm_18

宋红康MySQL高级篇 宋红康微博_jvm_19

【Java拥有自动内存管理的能力:自动分配内存 + 自动回收内存。程序员基本上不用参与其中,但要理解JVM内部结果,不然JVM出现问题之后,怎么去排查。】

宋红康MySQL高级篇 宋红康微博_java垃圾回收_20


宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_21

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_22

宋红康MySQL高级篇 宋红康微博_java垃圾回收_23

【方法区:就类似于一个接口。JDK7及之前的JDK版本,它的落地实现是永久代;JDK8及之后的JDK版本,它的落地实现时元空间。】

宋红康MySQL高级篇 宋红康微博_jvm_24

宋红康MySQL高级篇 宋红康微博_jvm_25

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_26

【这是很重要的一章!!!GC:garbage collection。GC:garbage collector。】

【GC的两个过程:垃圾的标记阶段,垃圾的清除阶段】【标记阶段的算法之间作比较,清除阶段的算法之间作比较】

宋红康MySQL高级篇 宋红康微博_GCRoots包含哪些对象_27

宋红康MySQL高级篇 宋红康微博_jvm_28

【因为只有堆和方法区才有可能存在垃圾(JVM的其他内存区域,比如 程序计数器、虚拟机栈、本地方法栈 都不会存在垃圾对象),所以会存在GC行为的只有堆和方法区。java虚拟机规范并不要求所有的JVM都回收方法区。频繁收集新生代,较少收集老年代,基本不动方法区。方法区,就类似于一个接口,JDK7及之前的JDK版本,它的落地实现是永久代;JDK8及之后的JDK版本,它的落地实现时元空间。】

宋红康MySQL高级篇 宋红康微博_java垃圾回收_29

【在JVM中究竟如何标记一个对象是不是已经死亡了呢?简答:当一个java对象已经不再被任何的存活对象继续引用时,就可以宣判为已经死亡。判断java对象存活的方式一般分为两种:引用计数算法和可达性分析算法。】

【怎么判断一个人的价值大不大?看一下这个人被需要的程度。你是唯一的存在,别人替代不了你,这个时候你的价值就很大】

宋红康MySQL高级篇 宋红康微博_GCRoots包含哪些对象_30

【无法解决循环引用,是引用计数算法的致命缺陷!】【问:对象头存放了哪些信息?】【时间复杂度+空间复杂度】

宋红康MySQL高级篇 宋红康微博_jvm_31

【循环引用;内存泄漏、内存溢出:、举一个内存泄漏的例子。】

注意:下面这个例子是用来证明java中是不是使用“引用计数算法”来进行垃圾回收?

宋红康MySQL高级篇 宋红康微博_GCRoots包含哪些对象_32

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_33

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_34

宋红康MySQL高级篇 宋红康微博_GCRoots包含哪些对象_35

 【为什么能被回收呢?如果java使用的是引用计数算法,则不能被回收;现在通过实验测试发现的确被回收了,说明我们使用反证法反正出来: java使用的不是引用计数算法。】

宋红康MySQL高级篇 宋红康微博_jvm_36

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_37

【强引用、弱引用、软引用、虚引用】

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_38

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_39

【关于可达性分析的重要的文字描述!需要记住!】

宋红康MySQL高级篇 宋红康微博_java垃圾回收_40

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_41

宋红康MySQL高级篇 宋红康微博_GCRoots包含哪些对象_42

【面试题:哪些对象可以作为GC Roots?】

宋红康MySQL高级篇 宋红康微博_java垃圾回收_43

宋红康MySQL高级篇 宋红康微博_java垃圾回收_44

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_45

宋红康MySQL高级篇 宋红康微博_java垃圾回收_46

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_47

【JDK中的finalize() 方法可以被任何一个对象重写,如果不重写,则该方法的方法体是空的】

宋红康MySQL高级篇 宋红康微博_jvm_48

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_49

宋红康MySQL高级篇 宋红康微博_GCRoots包含哪些对象_50

【面试题:虚拟机中的java对象一般可以处于三种状态: 可触及状态、不可触及状态、可复活状态,并简述它们的含义。】

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_51

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_52

宋红康MySQL高级篇 宋红康微博_jvm_53

【测试finalize( ) 方法:发现在finalize( )方法中可以救活对象。】

package com.atguigu.java;
//测试Object类中finalize()方法,即对象的finalization机制。
public class CanReliveObj {
    public static CanReliveObj obj;//类变量,属于 GC Root

    //测试场景1:finalize()方法注释掉。测试场景2:finalize()方法不注释掉。
    //注意:此方法只能被调用一次
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("调用当前类重写的finalize()方法");
        obj = this;//当前待回收的对象在finalize()方法中与引用链上的一个对象this建立了联系,所以它就成为了可触及对象,不能被回收了
    }

    public static void main(String[] args) {
        try {
            obj = new CanReliveObj();
            // 对象第一次成功拯救自己
            obj = null;
            System.gc();//调用垃圾回收器
            System.out.println("第1次 gc");
            // 因为Finalizer线程优先级很低,暂停2秒,以等待它
            Thread.sleep(2000);
            if (obj == null) {
                System.out.println("obj is dead");
            } else {
                System.out.println("obj is still alive");
            }
            System.out.println("第2次 gc");
            // 下面这段代码与上面的完全相同,但是这次自救却失败了
            obj = null;
            System.gc();
            // 因为Finalizer线程优先级很低,暂停2秒,以等待它
            Thread.sleep(2000);
            if (obj == null) {
                System.out.println("obj is dead");
            } else {
                System.out.println("obj is still alive");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

宋红康MySQL高级篇 宋红康微博_java垃圾回收_54

宋红康MySQL高级篇 宋红康微博_GCRoots包含哪些对象_55

宋红康MySQL高级篇 宋红康微博_java垃圾回收_56

   

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_57

宋红康MySQL高级篇 宋红康微博_java垃圾回收_58

MAT下载的站点:http://www.eclipse.org/mat

eclipse关于GCRoots的说明:http://127.0.0.1:62808/help/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Fwelcome.html

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_59

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_60

宋红康MySQL高级篇 宋红康微博_GCRoots包含哪些对象_61

package com.atguigu.java;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Scanner;
//测试dump堆内存文件:30多行代码,竟然有1700个GCRoots
public class GCRootsTest {
    public static void main(String[] args) {
        List<Object> numList = new ArrayList<>();
        Date birth = new Date();
        for (int i = 0; i < 100; i++) {
            numList.add(String.valueOf(i));
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("数据添加完毕,请操作:");
        new Scanner(System.in).next();
        numList = null;
        birth = null;
        System.out.println("numList、birth已置空(堆中的对象会因此发生改变),请操作:");
        new Scanner(System.in).next();
        System.out.println("结束");
    }
}

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_62

宋红康MySQL高级篇 宋红康微博_jvm_63

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_64

宋红康MySQL高级篇 宋红康微博_GCRoots包含哪些对象_65

【object4这个对象我已经不用了,但通过GC过程之后它就是被回收不了,这就造成了内存泄漏,如果有很多对象都存在内存泄漏,那么累积起来就会造成内存溢出。另:GC过程为啥会回收不了object4对象呢?是因为object4这个对象直接或者间接的被引用着,它存在着引用链,所以被回收不了。另:通常情况下,我们排查问题时,会专门看某一个对象到GCRoots的引用链。】

package com.atguigu.java;
import java.util.ArrayList;
//-Xms8m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
public class HeapOOM {
    byte[] buffer = new byte[1 * 1024 * 1024]; //1MB的数字
    public static void main(String[] args) {
        ArrayList<HeapOOM> list = new ArrayList<>();
        int count = 0;
        try{
            while(true){
                list.add(new HeapOOM());
                count++;
            }
        }catch (Throwable e){
            System.out.println("count = " + count);
            e.printStackTrace();
        }
    }
}

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_66

宋红康MySQL高级篇 宋红康微博_java垃圾回收_67

【前面讲解了标记阶段以及标记阶段使用的标记算法,接下来讲解清除阶段以及清除阶段使用的算法。面试经常问!】

【垃圾清除阶段的三大基本算法:(1)标记-清除算法(2)复制算法(3)标记-压缩算法。】

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_68

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_69

【标记的是可达对象,而不是标记垃圾对象。而且,在标记的时候,会把“可达对象的标识”记录在可达对象的对象头中。】

【标记-清除算法的过程:(1) 标记阶段:需要从GCRoots开始遍历(第一次遍历),标记出所有被引用的对象。(2) 清除阶段:需要对堆内存从头到尾进行线性遍历(第二次遍历),如果发现某个对象在其Header中没有被标记为可达对象,则将其回收。】

宋红康MySQL高级篇 宋红康微博_GCRoots包含哪些对象_70

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_71

宋红康MySQL高级篇 宋红康微博_java垃圾回收_72

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_73

宋红康MySQL高级篇 宋红康微博_GCRoots包含哪些对象_74

注意:再次分配新对象内存时,可以使用“空闲列表”方式。对象的实例化见博客:

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_75

宋红康MySQL高级篇 宋红康微博_java垃圾回收_76

【复制算法的过程:需要从GCRoots开始遍历(全程只做一次遍历),凡是能够遍历到的对象都是可达对象,都是存活的,于是按照顺序逐一把存活对象从区域A复制到“之前是完全空白的区域B”,并且,在复制可达对象至区域B的时候,是按照顺序连续存储在区域B的,保证了区域B的内存连续性 。当把区域A中可达对象全部复制到区域B之后,区域A对于我们来说已经无用了,所以直接清空区域A即可。那么,接下来JVM分配新的对象内存的时候直接从区域B开始分配就行了。等到下次垃圾清除的时候,整个过程反过来即可,所以,区域A和区域B是交换着使用,内存利用率为50%,相对来说比较低(可以说是:以空间换时间)。这里要注意:从GCRoots遍历可达对象的时候,一旦遍历到了,不需要给它再做标记了,直接把它复制到区域B即可。注意:再次分配新对象内存时,可以使用“指针碰撞”方式。

宋红康MySQL高级篇 宋红康微博_jvm_77

类似于新生代中的S0和S1区域:。新生代中的S0区域和S1区域使用的垃圾回收算法,实际上就是复制算法,记住!!!

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_78

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_79

【上面截图中的“特别的”,写的有问题,这里更正一下:如果系统中的可达对象非常非常多,此时复制算法的效率并不会很高。这是为什么呢?因为如果系统中的可达对象非常非常多,那么复制算法需要从区域A复制到区域B的存活对象的数量就会非常非常多,所以,此时的复制算法效率不会很高。因此,复制算法比较适用于 需要复制的存活对象数量不会特别多才行。】

【最糟糕的情况是:垃圾回收的一圈,发现系统中什么垃圾都没被回收掉,而且系统中的全部对象都平白无故的复制了一份过去,栈的局部变量表中维护的“对用引用地址”也都平白无故的全部更新了一遍,这不是无用的操作吗,效率很低。】

【所以,复制算法比较适用于 需要复制的存活对象数量不会特别多才行(也即:少部分对象存活、大部分对象死亡的场景)。思考一下发现,新生代中的对象大部分都是朝生夕死(70%-99%),恰好适合于复制算法!!!赞!!!另:老年代就不适合用复制算法,因为老年代中的对象大部分都是存活的!!!】

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_80

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_81

宋红康MySQL高级篇 宋红康微博_jvm_82

宋红康MySQL高级篇 宋红康微博_java垃圾回收_83

宋红康MySQL高级篇 宋红康微博_java垃圾回收_84

【标记-压缩算法的过程:(1) 标记阶段:需要从GCRoots开始遍历(第一次遍历),标记出所有被引用的对象,也即标记出哪些是可达对象。(2) 压缩阶段:把可达对象按照顺序依次移动过去,并从“内存最开始的位置”顺序存储。之后,再把边界之外的所有空闲空间都清理掉即可。由此可见,标记-压缩算法过程执行完之后,存活对象都是按照顺序依次排列在内存的一端,另外一端都是空闲区域,实现了内存的无碎片化。注意:再次分配新对象内存时,可以使用“指针碰撞”方式。

宋红康MySQL高级篇 宋红康微博_java垃圾回收_85

对象的实例化见博客:

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_86

宋红康MySQL高级篇 宋红康微博_GCRoots包含哪些对象_87

宋红康MySQL高级篇 宋红康微博_jvm_88

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_89

【综合来看,发现“复制算法”(应用在新生代垃圾回收)与“标记-压缩算法”(应用在老年代垃圾回收)还是不错的。】

宋红康MySQL高级篇 宋红康微博_java垃圾回收_90

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_91

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_92

宋红康MySQL高级篇 宋红康微博_java垃圾回收_93

宋红康MySQL高级篇 宋红康微博_jvm_94

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_95

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_96

宋红康MySQL高级篇 宋红康微博_jvm_97

宋红康MySQL高级篇 宋红康微博_GCRoots包含哪些对象_98

宋红康MySQL高级篇 宋红康微博_宋红康MySQL高级篇_99

宋红康MySQL高级篇 宋红康微博_java垃圾回收_100

宋红康MySQL高级篇 宋红康微博_java垃圾回收算法_101