今天是刘小爱自学Java的第53天,学的有点懵… 不过还是要感谢你的观看,谢谢你。 话不多说,开始今天的学习: JVM也就是Java虚拟机,它的内存结构这块知识点。
最终还是决定更加深入地学习下JVM,同时也用自己的理解详细地说明Java程序是如何运行的。 当然本人能力有限,只能说尽己之能学的越多越好,全文较长,共三千多字,这还是忽视了一些知识点的… 一、Java程序开发三步骤编写、编译和运行,图解如下: ①编写阶段 后缀名为.Java的文件,也就是所谓的源码。 但是Java虚拟机它并不认识.Java文件,Java虚拟机和Java语言其实并没有必然的联系。 其实我挺想吐槽的,你们长的这么像,竟然不认识? 那为何不认识呢? 按照我个人的理解:我们常说的代码、Java语言,其实本质上还是人类定义的一门语言,主要由英文组成,Java虚拟机本身并不认识它。 所以需要将其编译成Java虚拟机认识的语言,即.class文件。 ②编译阶段 后缀名为.class的文件,也就是所谓的字节码文件,这个概念太重要了,一定要记住。字节码文件,就可以理解成我们写代码一个类(接口、枚举、注释)里面的所有数据。 .class文件是如何来的? javac编译器编译而来的,它能将.java文件编译成.class文件,这样的话JVM也就能认识.class文件了。 我们平时写的代码其实也是Java源码,只不过开发工具很强大,将这三个阶段糅合在一起了,以IDEA工具为例: src,其实就是源码的意思,我们平时编写的类、接口、枚举、注解等文件,其实本质上就是.java文件。 利用开发工具中的Show in Explorer功能,可以找到计算机里对应的文件夹文件。 out,这个我还不太懂具体是什么意思,但是.class文件就在这个文件夹里面。 如果不用开发工具,我们需要用javac编译器将.java文件编译成.class文件; 开发工具等于是自动帮我们编译了,非常的方便,但是原理我们要明白。 既然讲到了.class文件,回顾下反射中刚学的Class对象 Class本身也是Java里的一个类,它就是指字节码文件。 注意:小写字母开头的class是Java里的一个关键字,创建一个类时类名前面都会由它来修饰。 所以Class类也就表示成:class Class{}; 前面是关键字,后面是类名。 最后用一个例子来说明: 我们在开发工具里面编写一个Student类; 那么在对应的文件夹下就会有一个Student.java文件,也就是源码;同时开发工具会给我编译一个对应的Student.class文件,也就是字节码文件; 而这个字节码文件会有唯一的一个Class对象,专门用来描述该字节码文件,在反射中可以拿来使用。 ③运行阶段 Java虚拟机是认识.class文件的。 但它具体是如何运行的呢? 就需要了解下JDK了,图解如下: ①JRE:Java Runtime Environment 翻译过来就是Java运行环境,包含JVM和运行时所需要的核心类库。 也就是说有了JRE就可以运行Java程序了,但是只能用来运行,如果出了bug,是没法修改的,所以需要JDK。 ②JDK:Java Development Kit 翻译过来就是Java程序开发工具包,包含JRE 和开发人员使用的工具。 前面提到的javac编译器就是Java工具,JRE中是没有javac编译器的,所以它没法修改程序,毕竟没有编译的话JVM是不认识的。 Java虚拟机是认识字节码文件的,并且本质上它就是一个字节码翻译器。 它可以将字节码文件翻译成各个系统(Windows系统、Mac系统、linux系统)对应的机器码; 这样的话就能确保字节码文件能在各个系统上正确运行。 这也就是Java所谓的跨平台特性的由来。 接下来我们就详细地了解下Java虚拟机。 二、JVM内存结构我们看oracle最新的官方文档,最权威的便是这个官方文档,毕竟技术是不断更新的: JVM内存主要分为这五大块:
其中官方文档中还有一个运行时常量池,暂且不考虑。 ①stack栈 常说的栈内存,其实严格上来说应该叫Java虚拟机栈。 利用开发工具IDEA断点调试,我们可以很清晰地看到方法进栈出栈的过程。 这是一段非常简易的代码:main方法里面调用method1方法,method1方法里面调用method2方法。 利用右上角两个功能键,可以一步一步地了解方法进栈出栈的整个过程。 Frames,也就是帧的意思,一个方法对应一个栈帧。 什么叫帧? 在影像动画里 ,一个镜头就是一帧;那么栈帧就可以理解成方法进栈的那一刻,就会形成一个栈帧。 每个方法进栈都会有一个对应的栈帧。 此外、栈是先进先出原则,mian方法在最底下,最先进来,最后出去。这个过程可以用断点调试模拟出来,感兴趣的小伙伴可以去尝试下。 其中方法栈里的线程是不共享的,什么意思呢? 最直接地理解就是:栈里面,不同的线程是独立存在的,所以线程安全。 相反的是堆、方法区线程是共享的,这又是什么意思呢? 最直接地理解就是:堆、方法区里面的数据,不同的线程都可以拿过去用,所以线程不安全。 ②heep:堆 这是翻译成中文后的页面,原文都是英文,英语好的小伙伴可以直接看,不过翻译大体上也还算是准确的:
此外对象是会创建很多的,其中一小部分会一直被使用到,大部分我们使用后就会舍弃,所以这也是垃圾收集器管理的主要区域。 根据这两种不同的情况,垃圾收集器为了更好地一一应对,堆内存又被划分成新生代和老年代:
其中还有更加详细地细分,实在是学不动了,暂不考虑。 ③方法区 同样的,看官方文档: 这个翻译有点问题,但大体上也能看:
也就是.class文件便是存储在这个内存区域。 说白了就是类里面的数据,都是存在这个方法区里面的。 类中有什么? 成员变量,成员方法等等,其中方法和变量还分静态和非静态,这些都是存储在方法区里面的。除了类里的数据,常量池也在方法区里面。
其中静态变量随着类的加载而加载,它并不会进堆。 以上三块便是和我们编写Java代码息息相关的内存区域,除此之外还有两块区域: ④程序计数器 我们平时编写的代码可以被反编译成JVM指令,cpu就是依靠这个程序计数器执行指令的;这一块很难接触到,做一个了解即可: 程序计数器会保存当前执行指令的地址,比如说程序计数器现在保存的为0,那么CPU会执行0对应的指令; 一旦指令执行,程序计数器将更新到下一条指令,上图中也就是3,CPU接着会执行3对应的指令。 就这样依次执行下去,程序计数器的作用就在于保证程序能正常运行。 ⑤本地方法栈 ①中的栈准确说应该叫虚拟机栈,它服务的是Java方法; 本地方法栈作用和其是差不多的,不同的地方在于它服务的方法不是Java方法而是本地方法。 什么叫本地方法?我们其实见过,如下图: Objcet类中就有本地方法,被native这个关键字修饰,native就是本地的意思,这很好记忆。 也就是说该方法不是由Java语言写的,而是由本地语言写的,比如说C语言。 当然虚拟机规范中并未给出强制规定由什么本地语言写,不同的虚拟机可以自由实现。 以上便是对JVM内存结的说明,除了JVM内存结构外,还有两大块…… 三、JVM体系结构源码通过编译成字节码文件,再被类加载器加载进内存,最后经过各种交互被执行,大致就是这么一个运行流程: ①类加载子系统(Class loader SubSystem) 它的作用是将类加载进内存,在Java中对应一个类ClassLoader,该类是一个抽象类。
②运行时数据区(Runtime Data Areas) 也就是JVM内存结构,上述已经详细讲解了,这也是对于程序员来说需要去重点了解的。 ③执行引擎(Execution Engine)
④本地方法接口和本地方法库 存放本地方法。 总结:以上便是对Java程序运行的详解,以及JVM内存分析。 奈何本人能力实在是有限,还有一些地方法没有弄明白。讲解的也不算很深入,但就我当前具备的能力而言,已经算是最详细的解读了。 谢谢你的观看。 如果可以的话,麻烦帮忙点个赞,谢谢你。 |
|
来自: 刘小爱v > 《Java学习笔记》