一个新手菜鸟的JVM 入门初体验

     话说现在jdk已经有好多个版本了,官网楼一眼:https://blogs.oracle.com/java-platform-group/the-arrival-of-java-13 已经13了. 以前很少关注专业的官网,学到的都是二手资料,也不知道对不对,还是得多逛逛官网,官网才是权威(英语渣渣的我,看官网就是悲催)。好了,言归正传,今天我们一起来聊聊JVM ,是本人的一点点浅见,还请各位看官大佬多多指教..

1、首先需要了解的就是java中的JDK ,JRE, JVM 这三者的关系?

 这里引用一个大佬的详细说明:https://www.cnblogs.com/iskandar/p/8933340.html

相信各位都知道这个内容的。

2、它又是如何转换的,让JVM认识的?java源码是如何进入到JVM中的?

  a. 编译: javac Test.java ---> Test.class   (源码 --> 类文件)

cmd -> javac Test.java   这个过程就是通过编译成类文件的(Test.class)的 ,这是一个怎样的过程呢?

   Test.java -> 词法分析器 -> tokens流 -> 语法分析器 -> 语法树/抽象语法树 -> 语义分析器 -> 注解抽象语法树 -> 字节码生成器 -> Test.class文件 

那么我们JVM 又是如何看懂这些内容呢? 应该就是有个对应的关系进行转换的。地址(通过这个地址我们就也知道这个是怎么来对应的的)https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html

 magic(魔数): cafe babe (固定值)
The  magic  item supplies the magic number identifying the  class  file format; it has the value  0xCAFEBABE . 

0000 0034   对应10进制的52,代表JDK 8中的一个版本

b.类文件到虚拟机(类加载机制

    1. 装载(Load)查找和导入class文件

   (1)通过一个类的全限定名获取定义此类的二进制字节流 (2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构 (3)在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口

   2.链接(Link)

   3. 验证(Verify)保证被加载类的正确性,如:文件格式验证、字节码验证 、 符号引用验证 等

   4.准备(Prepare) 为类的静态变量分配内存,并将其初始化为默认值  如:private static int a=10 (在这一步的时候,会先赋值a=0) 

   5.解析(Resolve) 把类中的符号引用转换为直接引用 ,通俗的讲就是:将这些标识符号转换成实际的直接引用(实际物理地址)

   6. 初始化(Initialize)将之前准备的 a=0  初始化为: a=10

见图:


 

这个地方需要说下:类装载

在装载(Load)阶段,其中第(1)步:通过类的全限定名获取其定义的二进制字节流,需要借助类装载 器完成,顾名思义,就是用来装载Class文件的。 (1)通过一个类的全限定名获取定义此类的二进制字节流 

类加载时,这个地方需要注意下:每次加载都是需要先加载父类,只有父类没有才找自己(个人理解)。官方说法叫:双亲委派机制

3、装载完之后,就进入了JVM 了,这里重点来了,有个概念叫:运行时数据区(Run-Time Data Areas)

说白了就是类文件被类装载器装载进来之后,类中的内容(比如变量,常量,方法,对象等这些数 据得要有个去处,也就是要存储起来,存储的位置肯定是在JVM中有对应的空间

Heap(堆)  -- (JVM内存的堆内存)针对jvm的只有一个,并且实线程共享的区域(线程是不安全的),主要存放类对象以及数组等。堆内存又分:Yang (Eden,s0(from),s1(to),占比(8:1:1)为了更好的处理内存碎片,保证内存空间的连续性),old
Method Area(方法区)  --(JVM内存的非堆内存即永久代) 针对jvm只有一个,并且实线程共享的区域(线程是不安全的),主要存放静态变量,类信息、常量、即时编译器编译后的代码等。
Run-Time Constant Pool(运行时常量池),属于线程层面的,存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放
PC Register(程序计数器)在出现多线程时,方便线程之间的切换,主要记录线程的执行到哪一步等,保存有当前正在执行的JVM指令的地址,为了线程切换后能够 恢复到正确的执行位置,每条线程需要有一个独立的程序计数器(线程私有)。 
Java虚拟机栈 (线程层面),Java栈的区域很小,只有1M,特点是存取速度很快,所以在stack中存放的都是快速执行的任务,基本数据类型的数据,和对象的引用(reference),①以帧为单位的压栈或出栈;②通过-Xss来设置, 若不够会抛出StackOverflowError异常。每一个被线程执行的方法,为该栈中的栈帧,即每个方法对应一个栈帧。调用一个方法,就会向栈中压入一个栈帧;一个方法调用完成,就会把该栈帧从栈中弹出
本地方法栈(线程层面),保存native方法进入区域的地址。

未完待续.........

以上如有描述不对的地方,还请各位看官大佬指出,谢谢!!

猜你喜欢

转载自blog.csdn.net/u010200793/article/details/103540680