• JVM 基础知识


    JVM 概念

    JVM(Java Virtual Mechine,Java虚拟机),是 Java 应用程序的运行时引擎。JVM 是 JRE(Java Runtime。Java运行环境)的一部分,它实际上就是去运行 Java 程序的 main 方法。

    为什么说 Java 是一个平台无关的语言? Java 号称是 Write Once。Run Anywhere,能实现这个的核心。就是 JVM 。当我们通过编译器编译 .java 文件时,会生成一个同名的 .class 文件(包含字节码),当我们运行 Java 程序时,流程如图所看到的:

    这里写图片描写叙述

    类载入器子系统

    类载入器子系统主要负责下面三部分:

    • 装载
    • 链接
    • 初始化

    装载

    类载入器读取 .class 文件。生成对应的二进制数据并将其保存在方法区域中。对于每一个 .class 文件,JVM 在方法区域中存储下面信息:

    • 已载入类及其直接父类的全名(含包名)
    • .class 文件是否与类或接口或 Enum 相关
    • 修饰符。变量和方法信息等

    载入完 .class 文件后,JVM 会创建一个类型为 Class 的对象。以便在堆内存中表示此文件。这里须要注意的是。该 Class 对象的类型是在java.lang包中提前定义的类。这个Class对象能够被程序猿用于获取类名,类名。方法和变量信息等类级别信息。我们能够通过 Object#getClass 方法来获取该对象。这也是 Java 反射的基础。关于反射可參见这篇文章

    /** 
     * java获取Class对象的三种方式 
     */  
    public static Class<?> getClassObj() {  
        // 依据类名获取Class对象  
        Class<?

    > clazz1 = People.class; // 依据对象获取Class对象 People people = new People(); Class<?> clazz2 = people.getClass(); // 依据完整类名获取Class对象 try { Class<?> clazz3 = Class.forName("com.yuyh.reflection.java.People"); } catch (ClassNotFoundException e) { Log.e(TAG, e.toString()); } Log.i(TAG, "clazz1 = " + clazz1); return clazz1; // clazz2 clazz3 }

    链接

    链接过程负责对二进制字节码的格式进行: 校验、初始化装载类中的静态变量、解析类中调用的接口、类。在完毕了校验后,JVM初始化类中的静态变量,并将其值赋为默认值。

    • 校验: 确保 .class 文件的正确性,检查此文件是否正确格式化并由有效编译器生成。假设验证失败,将会抛出运行时异常:java.lang.VerifyError
    • 初始化: JVM为类变量分配内存并将内存初始化为默认值。

      比如 public static int CODE = 1; 实际上。是将CODE初始化为0;

    • 解析: 通过搜索方法区域来定位被引用的实体。对类中的全部属性、方法进行解析,以确保其须要调用的属性、方法存在。以及具备应的权限。若方法或属性不存在,将会抛出 NoSuchMethodErrorNoSuchFieldError 等异常。

    初始化

    在这个阶段,全部静态变量都会分配他们在代码和静态块(假设有)中定义的值,也就是赋值。运行顺序是在类中从上到下运行,在类层次中从父类到子类运行。

    通常有三个类载入器:

    • 引导类载入器: 每一个 JVM 实现都必须有一个引导类载入器。用于载入载入 JAVA_HOME/jre/lib 文件夹中存在的核心 Java API 类。

      这个路径通常称为引导路径。

      该载入器通过 C。C ++ 等语言实现。

    • 扩展类载入器:用于载入扩展文件夹 JAVA_HOME/jre/lib/ext(扩展路径)或 java.ext.dirs 系统属性指定的不论什么其它文件夹中存在的类。该载入器通过 sun.misc.Launcher$ExtClassLoader 类实现。

    • 系统/应用程序类载入器:是扩展类载入器的子类。用来载入启动參数中指定的Classpath中的jar包以及文件夹。它也通过 sun.misc.Launcher$ExtClassLoader 类实现,对应的类名为 AppClassLoader

    举个栗子:

    public class Test{
    
        public static void main(String[] args) {
            System.out.println(String.class.getClassLoader());
            System.out.println(Test.class.getClassLoader());
        }
    }   

    输出:

    null
    sun.misc.Launcher$AppClassLoader@73d16e03

    由于 String 类是通过引导类载入器载入的。而引导类载入器是通过 C/C++ 实现的,并不是 Java 对象,所以为null;而我们自己定义的类是通过应用程序类载入器载入的。

    注:JVM遵循托付 - 层次原理来载入类。
    
    1. 系统类载入器 托付载入请求到 扩展类载入器
    2. 扩展类载入器 托付载入请求到 引导类载入器
    3. 假设类在引导路径中找到。类被载入,否则请求再次传递到扩展类载入器,然后到系统类载入器。最后假设系统类载入器无法载入类。那么我们得到运行时异常java.lang.ClassNotFoundException。

    如图所看到的:

    这里写图片描写叙述

    JVM 内存模型

    方法区(Method Area)

    在方法区域中,存储类名称、直接父类名称、方法和变量信息等全部类级信息,包含静态变量。

    每一个 JVM 仅仅有一个方法区。它是一个线程共享资源。

    对于每一个载入的类,会在方法区中保存下面信息:

    类型信息
      类及其直接父类的全限定名(java.lang.Object没有父类)
      类的类型
      类的訪问修饰符
      实现的接口的全限定名的列表
      字段与方法信息
      常量池
      除常量外的静态变量
      类的Class及ClassLoader引用
    
    字段信息
      字段名
      字段类型
      字段的修饰符(public, private , protected, static, final, volatile, transient)
    
    方法信息
      方法名
      方法返回类型
      方法參数的数量和类型(依照顺序)
      方法的修饰符(public, private, protected , static, final, synchronized, native, abstract)
    

    堆区(Heap Area)

    全部对象(包含数组)的信息存储在堆区域中。每一个 JVM 也有一个堆区域,而且它也是一个线程共享资源。堆区可能会抛出 OutOfMemoryError 异常。

    栈区(Stack Area)

    对于每一个线程,JVM 会创建一个存储在此处的运行时堆栈。每调用一个方法,就会生成一个栈帧(Stack Frame)用于存储方法的本地变量表、操作栈、方法出口等信息。当这种方法运行完后,就会弹出对应的栈帧。假设请求的栈的深度过大,虚拟机可能会抛出 StackOverflowError 异常。假设虚拟机的实现中同意虚拟机栈动态扩展,当内存不足以扩展栈的时候,会抛出 OutOfMemoryError 异常。在线程终止后。它的运行时栈将被 JVM 销毁。它不是共享资源。

    程序计数器(Program Counter Register)

    存储线程当前运行指令的地址。由于多线程间切换时要恢复每一个线程的当前运行位置,显然每一个线程都会有独立的PC寄存器。

    本地方法栈(Native Method Stacks)

    对于每一个线程,会创建单独的本地堆栈。它存储本地方法信息。与 Stack Area 相似。

    运行引擎

    运行引擎运行 .class(字节码)。它逐行读取字节代码,读取各类存储区中存在的数据和信息并运行指令。它能够分为三个部分:

    • 解释: 它逐行解释字节码,然后运行。

      主要缺点是当一个方法被多次调用时,每次都须要解释。(第一代JVM)

    • 即时编译(Just-In-Time Compiler,JIT): 用于提高解释器的效率。

      它编译整个字节码并将其更改为本机代码,因此每当解释器查看反复的方法调用时,JIT为该部分提供直接本地代码,因此不须要又一次解释,从而提高效率。(第二代JVM)

    • 垃圾收集器:它会销毁未引用的对象。

      关于垃圾收集,暂不叙述。

    Java Native Interface (JNI)

    它是与本地方法库交互并提供运行所需的本机库(C。C ++)的接口,使JVM 能够调用C / C ++库。

    本地方法库

    它是运行引擎所需的本机库(C。C ++)的集合。

  • 相关阅读:
    c#读取INI文件
    无题
    tnsping 命令解析
    宁静——一种心灵的奢望
    .NET框架类别
    19任意三个数的和为15
    VS.NET2005安装过程中遇到error:1935错误时的解决方法
    SQL Server 2005 : 存储过程传递字符串类型参数时,如何在参数中包含单引号
    SQL Server 2005 : 分割字符串
    网站发布在中文操作系统,但ReportViewer的工具栏显示为英文的解决方法
  • 原文地址:https://www.cnblogs.com/llguanli/p/7262711.html
Copyright © 2020-2023  润新知