美文网首页
JVM内存模型

JVM内存模型

作者: Richard_80ec | 来源:发表于2017-10-19 09:26 被阅读0次

    1、类的加载、连接和初始化

          加载:查找并加载类的二进制数据(字节码文件)

          连接:

                 验证:确保被加载的类的正确性(手工生成class文件,可

                            能不符合JVM标准规范)

                 准备:为类的静态变量分配内存,并将其初始化为默认值

                         (这个时候并不涉及new对象的操作)

                 解析:把类中的符号引用转换为直接引用(Child.run()转

                            换为pointer)

          初始化:为类的静态变量赋予正确的初始值(正确:用户赋予的

                        值)

          示例:MyTest

       1.1类的使用方式:

            所有的类或接口只有在java程序“首次主动使用”才会初始化

            主动使用:

                   (1)创建类的实例new Test();

                   (2)访问某个类的静态变量,或者为某个类的静态变量赋值:a =

                        Test.b; Test.b = a;

                   (3)调用类的静态方法:Test.doSomething();

                   (4)初始化一个类的子类:

                        class Parent{};class Childextends Parent{

                        public static int a= 3};

                        Child.a =4;

                  (5) java虚拟机就启动时被标明为启动类的类(java TestTest

                       中有main方法)

                  (6)反射Class.forName(“Test”);

             除此之外的使用都是被动使用,被动使用是不会触发初始化的。

       1.2类的加载:

            将.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,又 来封装在方法区内的数据结构,class对象是jvm在类加载时自动创建的。

      1.3类加载器

            Java虚拟机自带的类加载器:

           根类加载器(Bootstrap)(c++ java代码中无法获取该类)

           扩展类加载器(Extension)(java )

           系统类加载器(System)(也叫应用加载器)

           用户自定义的类加载器

       层次关系:

           根类加载器:用c++实现,并没有集成java.lang.ClassLoader类

           扩展类记载器:父类加载器为根类加载器,加载目录jre\lib\ext,如果把用户创建的jar包放在这个目录下,则会有扩展类加载器                                  进行加载。

           系统类加载器:父类加载器为扩展类加载器,从环境变量classpath或系统属性java.class.path所指定的目录加载类,是自定义                                   加载类的默认父类加载器。

          (1)核心类库由bootstrap加载器进行加载,获取到为空,其他都可获取,例Test2

          (2)调用一个类的静态变量,且这个静态变量在编译时期就可以确定,则不会进行类的初始化,当这个类在执行阶段才能确                     定,则会进行初始化,例Test4,Test3

          (3)加载顺序—启动类—父类—子类,例Test5,Test6

          (4)调用loadClass方法加载一个类,并不是对类的主动使用,并不会初始化该类。例Test7

      1.4类的加载机制—父委托机制

          (1)除了java自带的根类加载器,其余类加载器都有且仅有一个父类加载器,只有当父类加载器不能进行加载的时候,才会由                子类加载器进行加载。

          (2)父子加载器并非继承关系,子加载器并不一定继承父类加载器

          (3)安全考虑,每个类都会有确定的层次中的一个类加载器进行加载,自定义除外。

         (4)每个类都有自己的命名空间,命名空间由该类加载器及所有父类加载器所加载的类组成。同一个命名空间中,不会存在两                个完整名字相同的类,不同命名空间中,可能存在完整名字相同的类。

      1.5 用户自定义类加载器

            继承ClassLoader类,覆盖findClass方法,会由loadClass方法调用。

       1.6 JVM虚拟机执行整体顺序

    2、JVM的内存结构

    方法区和对是所有线程共享的内存区域;而java栈、本地方法栈和程序员计数器是运行是线程私有的内存区域。

    程序计数器:当前线程所执行字节码指令的行号指示器,线程私有,执行java方法,计数器记录的是java代码的行号,执行native方法则为null(undefined);

    虚拟机栈:线程私有,表示java方法执行的内存模型,每个栈帧对应的是一个调用的方法,包括局部变量表、动态链接、操作数栈、指向当前方法所属的类的运行时常量池、方法返回地址和附加信息。

    栈帧是保存在虚拟机栈中的,栈帧是用来存储数据和存储部分过程结果的数据结构,同时也被用来处理动态链接(Dynamic Linking)、方法返回值和异常分派(Dispatch Exception)。线程运行过程中,只有一个栈帧是处于活跃状态,称为“当前活跃栈帧”,当前活动栈帧始终是虚拟机栈的栈顶元素。

    局部变量表:方法参数,方法内部定义的变量。

    操作数栈:每一个方法的调用,都会建立一个操作数栈

    动态链接:与静态解析对应,一部分的符号引用会在运行是转化为直接引用

    方法返回地址:正常完成出口,异常完成出口

    本地方法栈:线程私有,与虚拟机栈类似,为native方法服务

    堆:线程共享,存储对象本身及数组

    方法区:线程共享,类信息,静态变量,常量,编译后的代码等

    运行时常量池:存放编译期生成的各种字面量和符号引用

    2.1对象的创建—内存分配

    (1)jvm遇到一个new指令,检查这个指令的参数是否能在常量池中定位到一个类的符号引用,然后检查这个类代表的类是否已被加载、解析和初始化。如果没有,则进行类加载。

    (2)类的加载—先加载父类,先加载静态块,顺序加载。

    (3)JVM开始为对象分配内存,两种方式:

    指针碰撞:java堆内存规整,GC带有压缩整理功能

    空闲列表:java堆内存空间并不规整。

    解决多线程分配内存的问题:

    失败重试:保证更新操作的原子性

    本地线程缓存取:TLAB,在启动线程时,就会在堆中给该线程预留一个内存空间

    (4)内存分配完后,首先内存空间都初始化为零值。若使用TLAB,则在分配TLAB时即已初始化成功。

    (5)设置对象头(Object Header):如何才能找到类的元数据信息,对象的哈希码,对象的GC分代年龄等信息。

    (6)执行方法,将对象初始化为指定的值。

    3、GC垃圾回收

    3.1判断对象失效

    (1)引用计数算法

    给对象添加一个引用计数器,每当有一个地方引用它,计数器值就加1,当引用失效就减1,计数器为0的对象就是不可能再被引用的对象。

    实现简单,但无法解决循环引用的问题

    (2)可达性分析算法

    通过一系列成为“GC ROOT”的对象作为起始点,向下搜索,走过的路径成为引用链,当一个对象到GC ROOT没有任何引用链相连时,则证明此对象是不可用的。

    可作为GC ROOT的对象有:

    虚拟机栈中引用的对象

    方法区中静态属性引用的对象

    方法区中常量引用的对象

    本地方法栈中JNI(native方法)引用的对象

    3.2垃圾回收算法

    (1)标记—清除算法

    先对不用的对象进行标记,然后清除

    (2)复制算法

    将内存按容量分为几块,在一个时间段只使用一块内存,当这块内存用完,则将内存清理干净,然后将可用的对象复制到另一个区域。


    (3)标记—整理算法

    复制算法在对象存活率较高时要进行较多的复制操作,效率将变低,而且浪费空间。

    标记—整理的标记过程与标记—清除一样,但后续步骤不是对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

    (4)分代收集算法

    根据对象存活周期的不同将内存划分为几块,一般把java堆分为新生代和老年代。

    在新生代中每次垃圾收集时都发现有大批对象死去,只有少量存活,就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代因为对象存活率搞,没有额外空间对它进行分配担保,就必须使用标记—清理或标记—整理算法来进行回收

    相关文章

      网友评论

          本文标题:JVM内存模型

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