JVM:
    运行在操作系统之上,与硬件没有交互关系!

    1. 1. 类加载器ClassLoader
    2. 负责加载class文件,文件的入口!
    3. 启动类加载器: 负责加载$JAVA_HOMEjre/lib/**rt.jar**里所有的class!
    4. 扩展类加载器: 负责加载$JAVA_HOME中jre/lib/**ext/*.jar 里所有的class!
    5. 应用程序类加载器(AppClassLoader)Java
    6. 这三种都是系统自带的!
    7. 用户自定义的加载器!
    8. 重点:加载过程:必会!
    9. 双亲委派机制:当接收到加载请求时,会向上找父类加载器去完成!如果父类加载器没有,则再找子类!
    10. "沙箱安全机制":不允许用户重写扩展类或启动类加载器中的类!
    11. 2. 执行引擎Execution Engine
    12. 解释命令,交个操作系统运行!
    13. 3. 本地接口Native Interface
    14. 方法有Native 修饰! 这个方法是由c,c++语言编写!
    15. 4. Native Method Stack
    16. 执行引擎发现有本地方法时,加载本地方法库! .dll 文件。
    17. 5. PC寄存器
    18. **用来存储指向下一条指令的地址**
    19. 执行引擎在执行的时候,读取的PC 寄存器的指令地址,可以忽略不计!
    20. 6. Method Area方法区 {非堆}
    21. 共享区间:
    22. 存储的内容:
    23. **静态变量+常量+类信息(构造方法/接口定义)+运行时常量池**存在方法区中
    24. But
    25. **实例变量存在堆内存**中,和方法区无关
    26. static修饰
    27. final 修饰
    28. class Stu{
    29. public result = 0;
    30. Stu(){
    31. };
    32. Stu(int id,String name,String addr){
    33. this.id = id;
    34. this.name = name;
    35. this.addr = addr;
    36. };
    37. private int id;
    38. private String name;
    39. private String addr;
    40. ...
    41. public void sayHi(){
    42. System.out.println("hello word");
    43. }
    44. public static void hello(){
    45. System.out.println("hello");
    46. }
    47. }
    48. Stu stu = new Stu(1,"张三","北京昌平");
    49. stu:实例变量!

    重点:
    JVM:

    栈:
        栈内存,生命周期与线程一样!
        栈中存储的数据:
            8种基本类型的变量+对象的引用变量+实例方法!
            int i = 10;
            stu ; 实例变量,对象的引用变量
            stu.sayHi(); 实例方法 / hello() 静态方法!
    
        数据以栈帧格式存在:
            本地变量:
            栈操作:
            栈数据
    
        运行原理:先进后出 或 后进先出
    
    
        Exception in thread "main" java.lang.StackOverflowError!栈内存溢出!
            通常来将都是代码的问题! 出现循环递归引用,或互相引用!
    
    
    堆:
        1.    介绍 栈堆方法区的关系!
                案例:
                    class Stu{
                        public result = 0;
    
                        //    构造存在方法区!
                        Stu(){
    
                        };
    
                        Stu(int id,String name,String addr){
                            this.id = id;
                            this.name = name;
                            this.addr = addr;
                        };
    
                        //    基本数据类型
                        private int id;    // 栈
                        //    String 是一个类,属于引用数据类型 
                        //  但是String name 类没有new String(); 这个数据应该存在 方法区
                        //  String s = new String("sss"); sss 存在堆中!
                        private String name;  
                        private String addr;
                        ...
    
                        //    栈
                        public void sayHi(){
                            System.out.println("hello word");
                        }
    
                        //    方法区
                        public static void hello(){
                            System.out.println("hello");
                        }
    
                        //    调用本地方法库,本地方法接口,本地方法 都是由 c/c++;
                        Thread.sleep(100);
                    }
                    Stu.class
                    Stu stu = new Stu(1,"刘德华","昌平");
    
                    通过类加载器 生成一个元数据模板!
                        Stu Class!
                        Stu stu1 = new Stu(1,"刘德华","昌平");
                        Stu stu2 = new Stu(2,"刘欢","昌平");
                        Stu stu3 = new Stu(3,"刘亦菲","昌平");
    
                        stu1 ,stu2, stu3 都是来在于同一个模板!Stu Class!
                        分析这个Stu Class 在哪? 在方法区!
    
                        stu1 ,stu2, stu3 实例变量 存在栈中!
    
                        (1,"刘德华","昌平");
                        (2,"刘欢","昌平");
                        (3,"刘亦菲","昌平");
    
                        以上数据存在堆!
    
        2.    堆的体系结构:
                堆的大小可以调节{内存调优指的就是堆内存调优}
                加载的时候将数据进行分区保存!
                jdk7 之前:
                    新生区:
                        是对象产生,消亡的过程!
                    养老区:
    
                    永久区:
                        不会产生垃圾回收!
                        永久区是方法区的一个实现!
    
                        jdk1.8 去永久代,将常量池放入堆中,引入了元空间 ,在本地内存中!元空间的大小受本地内存限制!
    
    
                    从 GC 角度划分:
                        Stu stu = new Stu(1,"刘德华","昌平"); //    会产生一个对象!
    
                        新生区:栈内存的1/3 !
                            伊甸区:新产生的对象 100 new Stu(); 
                            幸存0区 {form区/to区}: 存储没有被回收掉的对象!
                            幸存1区 {to区/form区}:
    
                            他们的比例关系: 8:1:1
    
                            当对象从from 区域 拷贝到 to区 的时候,这个对象年龄就会+1!
    
                        养老区:
                            默认经过15次GC 之后,依然存活的对象则会来到养老区!
                            MaxTenuringThreshold 默认值15 !
                            老年代不会频繁发生GC!
                            养老区如果满了,则会发生 FULL GC! 能够释放空间,能够存储对象了。如果不能释放空间了,但是还需要有对象进来,此时就会发生OOM!
                                OutOfMemoryError
    
                                产生原因:堆内存大小不够!
                                如果出现了内存溢出,那么解决方案!
                                    通过参数调整内存大小!
                                    -Xms、-Xmx
    
                    GC回收过程:必会!
                        MinorGC的过程:**复制** -> **清空** -> **互换**
                        from ,to区交换的时候,to区 总为空!
    
    
        3.    堆参数调优入门
                a.    -Xmx50m -Xms30m -XX:+PrintGCDetails
    
    
        4.    内存溢出分析工具:
                MAT:    介绍!
                jdk 自带工具: 找到哪个线程导致了OOM!
    
    
        5.    GC:分代收集算法!
                a.    如何判断对象可以回收! 是否已经死亡!
                    1.    可以使用引用计数法!
                            维护一个计数器,记录当前这个对象被引用的次数!引用一次 +1 , 取消了引用则 -1;
    
                            优点:
                                简单
                            缺点:
                                1.    +1 ,-1 性能低!
                                2.    循环引用依赖不好控制!
    
                            Java 中不使用!
                            Python 使用!
    
                    2.    可达性分析算法!
                            利用引用链与GC Roots 相连,如果能够链接到GC Roots 则表明当前对象不可回收,
                            如果不能链接到GC Roots 则表明可以回收!
    
                            重点: 真正标记以为对象为可回收状态至少要标记两次!
                                    第一次标记:找到未与GC Roots 相连的对象!
                                    第二次标记:判断当前这个对象是否实现了finalize(); 方法!如果实现了,这个对象也被标记了,可以回收!
    
                                finalize():
                                    当对象被回收了,会立刻这个方法!
    
                                System.gc(): 程序员手动调用做垃圾回收!
                                    当这个代码执行完,jvm 回立刻回收这个对象么?
                                    不是!
    
                                final  fianlly finalize 区别?
    
    
            GC 特点:
                - 次数上频繁收集Young区
                - 次数上较少收集Old区
                - 基本不动Perm区
    
    
                四种引用:
                    强引用:不会被回收!
                    软引用:内存快要OOM 了,才会回收软引用对象!
                    弱引用:无论内存是否足够都会回收掉只被弱引用关联的对象!
                    虚引用:被收集器回收时收到一个系统通知
    
                    记住前两种!
    
            垃圾回收算法:
                **Stop-the-World**
                    只要有GC执行,执行的应用程序所有现在都停止!等待GC 所需的线程执行完成之后,才能继续执行!
                    优化GC 算法 通常就是指:Stop-the-world发生的时间!
    
                   复制算法(Copying):新生区
                        优点:
                            - 实现简单
                            - 不产生内存碎片
                            - 速度快!
                        缺点:
                            - 浪费内存空间!
    
                    标记清除:老年代
                        优点:
                            节省空间
    
                        缺点:
                            需要两次扫描!
                            产生内存碎片!
    
                    标记压缩:老年代
                        优点:
                            节省空间
                            不会产生内存碎片!
    
                        缺点:
                            需要两次扫描!
                分代收集算法:
                    新生区: 复制算法!
                    老年代/养老区: 标记清除 或  清除与压缩混合使用!
    
        6.    垃圾收集器 了解!
                Serial/**Serial Old 串行收集器 单线程!
                ParNew收集器: 多线程!
                CMS收集器:  获取最短回收停顿时间为目标的收集器
                    具体做法:
                        - 初始标记(CMS initial mark)
                        - 并发标记(CMS concurrent mark)
                        - 重新标记(CMS remark)
                        - 并发清除(CMS concurrent sweep)
    
                        **优点**: 并发收集、低停顿
                        **缺点**: 产生大量空间碎片、并发阶段会降低吞吐量
    
                G1收集器:最好!
                    1.    并发与并行: 能够减少stop-The-World停顿时间!
                    2.    分代收集:    G1 有自己的收集方式!
                    3.    空间整合:  G1 没有内存碎片!
                    4.    可预测的停顿: 能够预知停顿时间!
    
                        收集过程:
                            1、初始标记(Initial Making)
    
                            2、并发标记(Concurrent Marking)
    
                            3、最终标记(Final Marking)
    
                            4、筛选回收(Live Data Counting and Evacuation)
    
                    垃圾回收器选择策略 :
                    客户端程序 : Serial + Serial Old;
                    吞吐率优先的服务端程序(比如:计算密集型) : Parallel Scavenge + Parallel Old;
                    响应时间优先的服务端程序 :ParNew + CMS。
                    G1收集器是基于标记整理算法实现的,不会产生空间碎片,可以精确地控制停顿,将堆划分为多个大小固定的独立区域,
                    并跟踪这些区域的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾最多的区域(Garbage First)。