ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

JVM入门请点进来,一起快乐学习JVM吧!

2021-03-17 12:57:56  阅读:151  来源: 互联网

标签:Java 字节 虚拟机 入门 线程 JVM 请点 加载


JVM

前言

以下所有内容,仅针对Hotspot虚拟机而言。

学习一门技术的时候,首当其冲,先百度一下:

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够"一次编译,到处运行"的原因。

OK,百度完了之后就得开始缩句了:

  • JVM就是二进制字节码的运行环境,在运行的时候,JVM是通过字节码进行运行的,不同的JVM的字节码格式不同,相同格式的字节码文件经过编译后,可以在符合该字节码格式的JVM上直接运行,不需要再次编译。
  • JVM只关心字节码文件,非Java语言的程序只要符合JVM的规范就可以在JVM上运行。
  • JVM是在操作系统之上的,跟硬件没有直接接触JVM。

当我们编写完一个程序后,这个程序是如何运行起来的呢,这里简述一下大概的流程:

  1. java程序先被javac编译器编译成.class文件(字节码文件)
  2. JVM运行字节码文件
  3. 经过类加载子系统将字节码文件加载到系统中
  4. 在运行时数据区分配好相应的资源
  5. 执行引擎做相应处理

类加载器子系统

一个字节码文件要被加载到系统中需要三个阶段:

加载阶段

该阶段的加载器分为三种,分别是引导类加载器,扩展类加载器,系统类加载器

三者的底层级别依次递增,即引导类加载器是扩展类加载器的父类加载器,扩展类加载器是系统类加载器的父类加载器。

引导类加载器

  • 这个类加载器是用C/C++编写的,主要作用是用来加载java的的核心库。
  • 负责加载<JAVA_HOME>\jre\lib\rt.ajr、resourse.jar或者是sun.boot.class.path路径下的内容。
  • 这是最底层的类加载器了,他没有父类加载器。

扩展类加载器

  • java语言编写。
  • 负责加载<JAVA_HOME>\jre\lib\ext目录下的类库或者系统变量"java.ext.dirs"指定的目录下的类库。

系统类加载器

  • java语言编写。
  • 负责加载java.class.path指定路径下的类库。
  • 该类是程序中默认的类加载库

双亲委派机制

可能会有小伙伴说这么多加载器,我写一个类都得用到这些加载器去加载吗?
JVM也帮我们想到了这个问题,他提供了双亲委派机制帮我们去解决,双亲委派机制的原理如下:

  • 当一个类加载器收到了类的加载请求时,不会马上去加载,而是将这个请求转给它的父类加载器。
  • 如果父类加载器还有父类加载器,则再次向上委派。
  • 直到找到引导类加载器,这时候就会判断引导类加载器是否可以完成这个类的加载,可以的就加载该类返回,否则就让子加载器去发挥作用。

优点:

  • 可以避免类被重复加载。
  • 保护程序的安全,避免核心API被人恶意修改。(因为核心类如果每个加载器都可以访问到的话,就可以根据自定义加载器去对他们进行修改,所以才要从最顶层的父类加载器开始判断)

沙箱安全机制

因为有了上述的双亲委派机制,所以JVM为我们提供了沙箱安全机制。
举个例子体验下吧!

在javaTest包下自定义一个String类,添加main方法


package javaTest;

public class String {
    public static void main(String[] args) {
        System.out.println("我是自定义的String类");
    }
}

此时我们是运行不了的,因为在加载String类的时候,会因为双亲委派机制一直委派到引导类加载器进行加载,加载的过程中,rt.jar中的String类找不到main方法,所以运行失败。

加载流程

  • 通过该加载类的全类名过去定义该类信息的二进制字节流
  • 通过该字节流所代表的静态存储结构转化为方法区(下面提及)的运行时数据结构
  • 在内存中生成一个代表该类的java.lang.Class的类,通过该类去调用方法区中该类的信息,作为数据的访问入口

链接阶段

链接阶段又可以细分为3个小阶段

验证

确保在上述加载阶段生成的Class对象中符合当前虚拟机要求,并且不会危害到虚拟机的安全。

准备

  • 类变量分配响应的内存并设置该类变量的默认初始值。
  • 这里不会为实例变量分配内存和默认初始化,实例变量的产生要等对象被创建后一起分配到堆中,而类变量的话是被分配到方法区中(JDK1.8前后发生了改变,下面方法区会谈到)。
  • 加入类变量被final修饰,则直接为变量赋值,而不是默认初始化。

解析

  • 将常量池中的符号引用转为直接引用

初始化阶段

  • 初始化阶段就是执行类构造器方法cinit()的过程。
  • 这里要注意的是cinit()并不是类中的构造器,这个方法是要当类中有静态代码块或者静态变量时,系统自动帮我们生成的,不用我们定义。

运行时数据区

这个区域是JVM重点关注的区域,可以说是重中之重,说他是核心也不为过。

JVM定义了在程序运行期间会使用到的运行时数据区,大体分为以下5块,这里面有的是线程独立的,随着线程生亡,有些是随着虚拟机生亡;

线程独立的数据区有虚拟机栈,本地方法栈,程序计数器;

虚拟机栈

本地方法栈

程序计数器

这个区域是运行时数据区最小的一个区域,只是用来记录线程的执行地址。

CPU工作的时候需要不停的去切换各个线程,当我们其他线程执行好了后就得回来执行当前的线程,所以就得直到之前执行到哪了,从程序计数器去获取到指定的地址,从那开始执行,避免重复工作。这也是为什么程序计数器是线程私有的,这样才能保证每个线程在进行切换时能够继续上次的工作往下执行,不会线程之间互相干扰。

今天先到这

方法区/元空间

执行引擎

标签:Java,字节,虚拟机,入门,线程,JVM,请点,加载
来源: https://blog.csdn.net/fighting32/article/details/114916774

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有