yunshuipiao / Potato

Read the fucking source code for the Android interview
Apache License 2.0
80 stars 12 forks source link

JMM, JVM, Java ClassLoader #27

Open yunshuipiao opened 5 years ago

yunshuipiao commented 5 years ago

JMM, JVM, Java ClassLoader

JVM 内存结构

Java 代码时运行在 jvm 虚拟机上,而虚拟机在执行 java 程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域有各自的用途。其中有些区域随着虚拟机的启动而存在,而有些区域则是依赖用户线程启动和结束而建立和销毁。

QQ20180624-150918

上面就是 JVM 运行时内存区域的结构。

Java 内存模型

在前面的 JVM 内存结构中,Java 堆和方法区是多个线程共享的数据区域。也就意味着多个线程可能操作保存在方法区中的同一个数据。这也就是常说的 Java的线程间通过共享内存进行通信。

Java 的多线程之间是通过共享内存进行通信的,而由于采用共享内存进行通信,在通信过程中会存在一系列如可见性、原子性、顺序性等问题,而JMM就是围绕着多线程通信以及与其相关的一系列特性而建立的模型。JMM 定义了一些语法集,这些语法集映射到Java语言中就是 volatile、synchronized 等关键字。

11

内存间相互操作

关于主内存和工作内存之间具体的交互协议,即一个变量如何从主内存拷贝到工作内存,如果从工作内存同步回主内存的实现细节,JMM 定义了 8 种操作来完成。虚拟机必须保证每一种操作都是原子的。

Java 对象模型

Java 是一种面向对象的语言,而 Java 对象在 JVM 中的存储也有一定的结构,称为 Java 对象模型。

每一个Java类,在被 JVM 加载的时候,JVM 会给这个类创建一个 instanceKlass,保存在方法区,用来在JVM层表示该 Java 类。当我们在 Java 代码中,使用 new 创建一个对象的时候,JVM 会创建一个 instanceOopDesc 对象,这个对象中包含了对象头以及实例数据。

20170615230126453

类加载的过程

在下述情况下类必须进行初始化:

具体过程

  1. 加载阶段
    1. 通过一个类的全限定名来获取定义此类的二进制字节流
    2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
    3. 在内存中生成一个该类的 Class 对象,作为方法区这个类的各种数据的访问入口。
  2. 验证:确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,不会危害虚拟机安全。
    • 文件格式验证:魔数,版本号,常量池等等
    • 元数据验证:父类是否允许继承,字段是否矛盾等等
    • 字节码验证:确认程序语义是否合法
  3. 准备:正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。
  4. 解析:虚拟机将常量池内的符号引用替换为直接引用的过程。
  5. 初始化:类加载过程的最后一步,真正执行类定义中的 Java 程序代码。

类加载器

双亲委派模型

从 java 虚拟机的角度来讲,只有两种不同的类加载器:启动类加载器(Bootstrap ClassLoader),使用 C++ 语言实现;所有其他的类加载器:java 语言实现,独立于虚拟机外部,继承 ClassLoader。

其工作过程是:如果一个类加载器收到类加载的需求,不会自己尝试加载这个类,而是把该请求委托给父类加载器去完成,每一个层次的类加载器都是如此。因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成加载时,子加载器才会尝试自己去加载。

好处在于:安全,防止出现自定义加载器出现多个相同的类,导致程序混乱。

总结

JVM 内存结构,和 Java 虚拟机的运行时区域有关。

Java 内存模型,和 Java 的并发编程有关。

Java 对象模型,和 Java 对象在虚拟机中的表现形式有关。