From a6128e75260f297a9d1186084a55f764109d1901 Mon Sep 17 00:00:00 2001 From: Omooo <869759698@qq.com> Date: Tue, 23 Jun 2020 14:58:19 +0800 Subject: [PATCH] =?UTF-8?q?Update=20JVM=20=E7=9B=B8=E5=85=B3=E5=8F=A3?= =?UTF-8?q?=E6=B0=B4=E8=AF=9D.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blogs/Java/口水话/JVM 相关口水话.md | 30 ++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/blogs/Java/口水话/JVM 相关口水话.md b/blogs/Java/口水话/JVM 相关口水话.md index ced1bc1..05e8eb9 100644 --- a/blogs/Java/口水话/JVM 相关口水话.md +++ b/blogs/Java/口水话/JVM 相关口水话.md @@ -7,9 +7,10 @@ JVM 相关口水话 3. 内存分配回收策略 4. Java 对象的创建、内存布局和访问定位 5. GC -6. 类加载机制及双亲委派模型 -7. 编译器优化 -8. 虚拟机相关 +6. 类加载机制 +7. 双亲委派模型 +8. 编译器优化 +9. 虚拟机相关 1. HotSpot 及 JIT 2. Dalvik 3. ART 及 AOT @@ -62,3 +63,26 @@ Full GC/Major GC 指发生在老年代的 GC,出现 Full GC 经常会伴随着 最后是对象的访问定位,Java 程序需要通过栈上的 reference 数据来操作堆上的具体对象,由于 reference 类型在 Java 虚拟机规范中只规定了一个指向对象的引用,并没有规定这个引用应该通过什么方式去定位和访问堆中的对象,所以对象访问方式也是取决于虚拟机实现而定。目前主流的方式有使用句柄和直接指针两种。使用句柄,就是相当于加了一个中间层,在对象移动时只会改变句柄中的实例数据的指针,reference 本身不需要改变。HotSpot 使用的是第二种,使用直接指针的方式访问的最大好处就是速度很快。 +#### 类加载机制和双亲委派模型 + +虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验、解析和初始化,最终形成可以被虚拟机直接使用的 Java 对象,这就是虚拟机的类加载机制。 + +类加载流程分为五个阶段,分别是加载、验证、准备、解析和初始化。 + +加载阶段,就是通过一个类的全限定名来获取定义此类的二进制字节流,将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。加载阶段是开发人员可控性最强的阶段,因为开发人员可以自定义类加载器。对于数组而言,情况有所不同,数组类本身不通过类加载器创建,它是由 Java 虚拟机直接创建。 + +验证是链接阶段的第一步,这一阶段的目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。它包括文件格式校验、元数据校验、字节码校验等。 + +准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。需要注意的是,这时候进行内存分配的仅仅包含类变量,不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在 Java 堆上。其次,这里所说的变量初始值是该数据类型的零值。 + +解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。符号引用以一组符号来描述所引用的目标,直接引用可以是直接指向目标的指针。 + +初始化阶段是执行类构造器 \() 方法的过程。\() 方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的。虚拟机会保证一个类的 \() 方法再多线程环境中被正确的加锁同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的 \() 方法,其他线程都需要阻塞等待,这也是静态内部类能实现单例的主要原因之一。 + +#### 双亲委派模型 + +双亲委派模型的工作过程是:如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一层次的类加载器都是如此,因此所有的类加载请求最终都应该传送给顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。 + +使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是 Java 类随着它的类加载器一起具备了一种带有优先级的层次关系。比如 Object 类,无论哪个类加载器去加载,应用程序各种加载器环境中都是同一个类,同时也避免了重复加载。而且,双亲委派模型也保重了 Java 程序的稳定运作。比如在应用程序中你是不能直接使用 UnSafe 这一不安全操作的类的。 + +双亲委派模型的实现相对简单,代码都集中在 ClassLoader 的 loadClass 方法中先检查是否已经被加载过了,如果没加载则先调用父加载器的 loadClass 方法,若父加载器为空则使用默认的启动类加载器作为父加载器。如果父加载器加载失败,抛出 ClassNotFoundException 异常,然后调用自己的 findClass 方法进行加载。 \ No newline at end of file