JVM前奏篇(大局观)

话不多说直接上干货,先来看oracle官网中是怎么描述JDK的:https://docs.oracle.com/javase/8/docs/index.html

这是官网中JDK、JRE、JVM的一个关系图

看了这张图我们要学什么呢?

以前我们在用java开发代码的时候只知道把JDK下载下来然后进行写代码,其实这样我们只用到了JDK的一个Tools,也就是上图中的Tools,我们只站在了JDK的一个应用层,那么现在这篇博客的目的就是要我们站在Java Virtual Machine(JVM)层面去做开发。

想一个问题,我们的电脑是怎么加载和识别.java文件的呢?

故事就从这里开始:

当我们编写一个.java文件后首先通过上图中最上面的Javac去编译成.class文件,然后也是通过上图中最上面的java进行去运行(这时候只是停留在java的一个应用层面),现在有一定的开发经验以后我们要把眼光放在底层的JVM当中,因为我们的代码最终是运行在JVM当中的,有必要去谈谈JVM了。

首先来看看在jdk中javac是怎么将java文件编译成class文件?【了解内容】

词法分析==》语法分析==》语法树==》字节码生成器生成class文件(这块作为了解就行了,个人也没有深究它)

下图是我随意找的一个class文件,看不懂没关系,简单了解一下他是怎么生成的:

 

他是机器所能识别的16进制的字符,前面的cafe babe是class的文件的格式,任何一个class文件都会有cafe babe,后面的一些是根据一些特有的符号组成的(怎么组成的这里不讨论,官网https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html中有)

下面来看下class文件是怎么被加载的?

1、先找到类所在位置(全路径)

2、装载它通过ClassLoader(怎么装载?ClassLoader的双亲委派机制(jdk自带的java.net包下面的String类,若自己也有一个java.net包且有一个String类,这样就会有冲突,所以需要双亲委派机制只加载一次),先自底向上寻找父类知道找到它的祖宗,然后找到就自顶向下一层一层返回,必须保证只被加载一次)

3、解析和初始化(初始化数据在运行时数据区)

将class里面的内容进行打散放入JVM的运行时数据区【看下图】:

 

方法区:方法区是所有线程共享的内存区域,带有static关键字的都放在方法区中,比如静态文件,常量池,类信息(当方法区内存不够用则抛出OutOfMemoryError(OOM)内存溢出异常)

堆:凡是new出来的对象或者数组都在堆内存中,他也是所有线程共享的内存区域(堆区内存不够用则抛出OutOfMemoryError(OOM)内存溢出异常

虚拟机栈:它是线程的执行区域(当一个线程被创建的时候虚拟机栈也就被创建了,他的生命周期是随着线程的结束而结束)

本地方法栈:它是当java调用C语言的时候创建的,也就是native关键字(这个作为了解,没多大意义)

程序计数器:多个线程之后CPU的一个计数器,他记录了你每个方法或者线程执行到哪里了,比如我有2个线程在同时执行,cpu在执行中只能执行一个线程,只不过他执行速度很快在两个线程中进行切换让我们感觉不出来,那么当他执行某个线程的时候突然阻塞了,cpu去执行另外一个线程将他唤醒,那么他怎么知道我这个线程执行到哪里了呢?,这个就是程序计数器的作用,他会对你当前线程执行到哪里了做一个标记记录你执行的位置(作为了解,没多大意义)

 

总结:JDK包含了JRE和java的Tools组件,JRE包含了一些常用的jar包和JVM

JDK通过Javac进行将java文件编译成class文件,通过classloader进行加载,加载完毕将class的内容打散放入jvm中,其中class的信息静态文件常量放在方法区,对象放在堆中,虚拟机栈相当于线程。

特别说明:子JDK1.8开始方法区变成了元空间。

posted on 2019-12-11 11:27  冰龙之剑  阅读(209)  评论(0编辑  收藏  举报

导航