美文网首页
JVM内存模型详解

JVM内存模型详解

作者: 雁度寒崖 | 来源:发表于2019-10-10 11:21 被阅读0次

    Java反射机制

    在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
    简而言之:就是把类里面的方法变量转化成

    public void sayHi(String helloSententce) {
            System.out.println(helloSententce + " " + name);
        }
    ...
    private String throwHello(String tag) {
            return "hello " + tag;
        }
    
    //调用实现以上方法
            Class<?> rc = Class.forName("com.czb.Robot");
            System.out.println(rc.getName());
            Robot r = (Robot) rc.getDeclaredConstructor().newInstance();
            Method getHello =rc.getDeclaredMethod("throwHello", String.class);
            getHello.setAccessible(true);
    //invoke默认返回Object
            Object str = getHello.invoke(r,"Bob");
            System.out.println("getHelloresult is "+str);
            Method sayHi = rc.getMethod("sayHi", String.class);
            Field name = rc.getDeclaredField("name");
            name.setAccessible(true);
            name.set(r,"ALice");
            sayHi.invoke(r,"Welcome");
    

    getDeclaredMethod来获取除了继承或实现的的所有方法
    getMethod只能获取public方法,包括继承或者实现的方法

    ClassLoader的作用

    通过将.class文件的二进制数据流装载进系统,然后交给Java虚拟机进行连接、初始化等操作。
    1.BootstrapClassloader:C++编写,加载核心库java.*
    2.ExtClassLoader:Java编写,加载扩展库javax.*
    3.AppClassLoader:Java编写,加载程序所在目录,加载路径(classpath)
    4.自定义ClassLoader:Java编写
    为什么要使用双亲委派机制去加载类
    避免多分同样的字节码的加载
    例如:System静态Class字节码,只需要一份就可以了,第一次加载是在BootstrapClassLoader
    隐式加载:new
    显示加载:loadClass,forName
    Java9之前newInstentce生成实例
    Java9之后getDeclaredConstructor().newInstance()

    ▲类装载过程(比较loadClass和forName)

    1.ClassLoader加载.class,生成Class对象,

    加载到内存中,并将这些静态数据转化成运行时数据区中方法区的类型数据,在运行时,数据区堆中生成一个代表这个类的Java.lang.class对象作为方法区类数据的访问入口

    2.链接:

    校验:检查加载的class的正确性和安全性;
    准备:为类变量分配存储空间并设置类变量初始值,类变量(static)随类型信息存放在方法区中,生命周期很长,使用不当容易造成内存泄漏
    解析:JVM将常量池内的符号引用转换为直接引用(不一定非要解析)

    3.初始化:执行类变量赋值和静态代码块
    ▲forName初始化完毕,loadClass只完成加载,还没有链接

    loadClass可以快速加载配置文件;
    如果你要连接Mysql,driver有一段static代码段,所以要用forName进行加载(因为静态代码段是第三步初始化里面完成的)

    ▲Java内存模型(JDK8)

    私有内存区域
    1.程序计数器(线程私有,不会内存溢出)

    当前线程所执行的字节码行号指示器(逻辑)
    通过改变计数器的值来选取下一条需要执行的字节码指令(分支、循环、跳转、异常处理、线程恢复)
    对Java方法计数,如果是Native方法,计数器的值为Undefined

    2.Java虚拟机栈(线程私有)

    Java方法执行的内存模型
    每个方法(字节码指令)执行时,都会创建一个栈帧(存储:局部变量表、操作数栈、动态连接、返回地址),栈帧持有局部变量和部分结果以及参与结果的调用和返回,方法调用结束时,栈帧就会被销毁
    局部变量表:包含了方法执行过程中的所有变量
    操作数栈:在执行字节码指令过程中,类似原生CPU寄存器,JVM字节码大部分时间都在操作数栈的操作上(入栈、出栈、复制、交换、产生消费变量),

    3.本地方法栈

    native方法

    共享内存区域
    1.元空间(MetaSpace)和永久代(PermGen)

    两者都是来存储class的相关信息(Methon和Field),
    ▲‘两者均是方法区的实现,Java 8之后元空间替代了永久带
    元空间用的是本地内存,永久带用的是JVM内存,也就是说本地内存多大,理论上元空间就可以有多大,这样就直接解决了空间不足的问题
    ●字符串常量池存在永久带中,容易出现性能问题和内存溢出
    ●类和方法的信息大小难以确定,给永久带的大小指定带来困难
    永久带会为GC带来不必要的复杂性,回收效率偏低
    ●方便HotSpot与其他JVMJrockit的继承

    Java堆(Heap)

    存放对象实例
    物理内存可以不连续
    是GC管理的主要区域(GC堆,垃圾堆)
    ●方法区(No-Heap)存在堆里

    JVM 三大性能调优参数-Xms, -Xmx, -Xss

    -Xms:堆的初始值(不够会扩容,扩容时会内存抖动)
    -Xmx:堆能到达的最大值
    -Xss:规定每个线程虚拟机栈(及堆栈)的大小(一般情况256k就可以了,会影响并发线程数的大小)

    Java内存中堆和栈的区别

    静态存储:编译时确定每个数据目标在运行时的存储空间需求
    栈式存储:数据区需求在编译的时候未知,运行时进入一个程序模块前必须知道其大小
    堆式存储:编译时、运行时都不知道所需内存大小,动态分配
    ●存储
    堆:创建好的对象和数组都会存到堆里面
    栈:基本数据类型,引用对象、数组时,栈定义变量保存在堆中目标的首地址,局部变量,参数放在堆栈里面。
    ●管理方式
    栈自动释放,堆需要GC
    ●空间
    栈比堆小
    ●碎片相关
    栈产生的碎片远小于小于堆(GC垃圾回收器不是实时的)
    ●分配方式
    栈支持静态分配和动态分配,而堆只支持动态分配
    ●效率
    栈比堆效率高,栈不够灵活

    intern函数到jdk6+后的改变

    如果先前在常量池已经创建了,则返回池中该字符串的引用。
    (Java 7之后常量池从方法区(之前还是永久带)移到了堆里面)

    String s1 = new ("asasas")//
    s1.intern();
    String s2 = "asasas";
    

    "asasas"
    否则就看他在不在Java堆里面,如果有就添加到常量池并返回该引用,如果堆里面没有,就在池里创建并返回引用(多了一个创建)

    相关文章

      网友评论

          本文标题:JVM内存模型详解

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