美文网首页
JVM 深入理解(一)

JVM 深入理解(一)

作者: only_one | 来源:发表于2020-06-13 10:14 被阅读0次
    简介:
    Java Virtual Machine JVM 全称 Java Virtual Machine,也就是我们耳熟能详的 Java 虚拟机。它能识别 .class后缀的文件,并且能够解析它的指令,最终调用操作系统上的函数,完成我们想要的操作。
    翻译:Java 程序不一样,使用 javac 编译成 .class 文件之后,还需要使用 Java 命令去主动执行它,操作系统并不认识这些 .class 文件。所以JVM就是一个翻译。
    image.png
    从图中可以看到,有了 JVM 这个抽象层之后,Java 就可以实现跨平台了。JVM 只需要保证能够正确执行 .class 文件,就可以运行在诸如 Linux、Windows、MacOS 等平台上了,JVM讲.class和.jar进行翻译成操作系统能识别的机器码,从而调用操作系统的方法。
    JVM、JDK,JRE之间的关系:
    JVM:只是一个翻译,把Class翻译成机器识别的代码,JVM 不会自己生成代码,需要大家编写代码,同时需要很多依赖类库,这个时候就需要用到JRE。(翻译)
    JRE:它除了包含JVM之外,提供了很多的类库(就是我们说的jar包,它可以提供一些即插即用的功能,比如读取或者操作文件,连接网络,使用I/O等等之类的)这些东西就是JRE提供的基础类库。JVM 标准加上实现的一大堆基础类库,就组成了 Java 的运行时环境,也就是我们常说的 JRE(Java Runtime Environment)。(提供类库)
    JDK:提供了一些非常好用的小工具,比如 javac(编译代码)、java、jar (打包代码)、javap(反编译<反汇编>)等。这个就是JDK(提供工具)
    JVM整体流程图:
    流程图.png
    图中的运行时数据区:jvm所管理的内存。
    执行引擎:将放在运行时数据区的中的方法,和操作进行解释执行。
    解释执行与JIT之间的区别:
    以HelloWord.java为例,通过javac进行编译之后得到.class文件,通过ClassLoader进行类加载到运行时数据区中(jvm所管理的内存),jvm再通过执行引擎将运行时数据区中的数据进行翻译机器码,从而执行调用操作系统的方法。那么还是没有说到解释执行和JIT 呢?
    翻译:
    (1)、将所有的类都全部翻译成机器码之后才执行,(速度慢,效率低)
    (2)、一遍翻译一遍执行,(相比第一种速度上有了很大的提升)-----解释执行。
    那什么又是JIT呢?
    在hotspot中,可能会有热点方法,如我们同一个方法执行很多次,jvm会将这部分代码直接编程成本地代码,提高运行速度,(前提是热点方法,热点类)
    运行时数据区:
    运行时数据区的定义:
    Java虚拟机在执行Java程序 的过程中会把它所管理的内 存划分为若干个不同的数据 区域。
    运行时数据区的类型:程序计数器、虚拟机栈、本 地方法栈、Java堆、方法区 (运行时常量池)、直接内存。
    如图所示:
    image.png
    1、程序计数器:当前线程执行的字节码的行号指示器;各线程之间独立存储,互不影响。
    举个例子:现有一个Person类,Person中提供了对应的方法,通过javac编译之后得到对应的.class文件,现在通过javap -c 对.class 文件进行反编译,如图所示:
    image.png
    上图所示是通过反编译得到.class文件的对应的字节码,每行都有对应的编号,在多线程中,cpu调度线程的,时间轮转时线程通过程序计数器记录当前的对应的行号,直到线程下次拥有cpu调度时接着执行,从而确保程序的正常执行。
    2、虚拟机栈:存储当前线程运行方法所需的数据,指令,返回地址,栈(先进后出)
    2.1、栈帧(方法)
    2.1.1、局部变量表:用来存储局部变量 只能 存储8大基本类型,对象引用
    2.1.2、操作数栈:存放方法的操作、执行。
    2.1.3、动态链表::Java语言特性多态(需要类运行时才能确定具体的方法)。
    2.1.4、完成出口:正常返回(调用程序计数器中的地址作为返回)、异常的话(通过异常处理器表<非栈帧中的>来确定)
    3、方法区:
    方法区主要存储:类信息,常量,静态变量,即时编译期编译后的代码。
    4、堆:
    堆主要存储:几乎所有对象实例,数组。
    方法区和堆为啥不用一个区域,要用两个区域?堆可以进行频繁的回收,方法区回收比较难,动静分明。
    从底层深入理解运行时数据区:
    image.png
    1、申请内存,分别给堆,栈,方法区分配内存。
    2、类加载,class进入方法区。
    3、常量,静态变量 入方法区。
    4、虚拟机栈帧--入栈帧
    下面先介绍一下HSDB工具,查看内存的工具:
    1)、首先找到jdk的安装目录,找到sawindbg.dll文件,将改文件复制到对应目录的jre下。
    image.png
    2)、现在采用JVMObject.java为例:
    public class JVMObject {
        public final static String MAN_TYPE = "man"; // 常量
        public static String WOMAN_TYPE = "woman";  // 静态变量
    
        public static void  main(String[] args)throws Exception {//栈帧
            Teacher T1 = new Teacher();//堆中   T1 是局部变量
            T1.setName("Mark");
            T1.setSexType(MAN_TYPE);
            T1.setAge(36);
            for (int i=0;i<15;i++){//进行15次垃圾回收
                System.gc();//垃圾回收
            }
            Teacher T2 = new Teacher();
            T2.setName("King");
            T2.setSexType(MAN_TYPE);
            T2.setAge(18);
            Thread.sleep(Integer.MAX_VALUE);//线程休眠很久很久
        }
    }
    
    class Teacher{
        String name;
        String sexType;
        int age;//堆
    
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    
        public String getSexType() {
            return sexType;
        }
        public void setSexType(String sexType) {
            this.sexType = sexType;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
    }
    
    3)、在jdk下面的lib目录找到a-jdi.jar文件,输入命令如下:
    image.png
    4)、让JVMObject运行,再将环境切换到JVMObject对应的目录下输入命令如下所示:
    image.png
    5)、在HSDB输入框中输入JVMObject对应的10756,并点击main方法
    image.png image.png
    如图所示为jvm虚拟机的栈区图,左边对应的是内存的地址。
    image.png
    main方法对应栈帧的内存地址,图上怎么还会有一个栈帧?原因是在代码中还调用了sleep方法,查看 sleep源码属于本地方法,验证了本地方法栈和hotspot栈合并。
    再看class在内存中所在的区域
    image.png image.png
    通过点击Teacher可以看到有两个地址,在回看JVMObject代码,在代码中创建了两个对象,mark和king对象。为了验证随便点击一个进行查看:
    image.png
    接下来看看堆空间
    image.png
    从图上可以看出,分为两个空间,新生代,和老年代,并且内存地址还是连续的,JVMOject代码中mark对象通过调用gc进行回收15次,则mark对象进入了老年代,而king在新生代。
    image.png
    总结:
    以栈帧的方式存储方法调用的过程,并存储方法调用过程中基本数据类型的变量(int、short、long、byte、float、double、boolean、char等)以 及对象的引用变量,其内存分配在栈上,变量出了作用域就会自动释放; 而堆内存用来存储Java中的对象。无论是成员变量,局部变量,还是类变量,它们指向的对象都存储在堆内存中。

    相关文章

      网友评论

          本文标题:JVM 深入理解(一)

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