Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JMM, JVM, Java ClassLoader #27

Open
yunshuipiao opened this issue May 20, 2019 · 0 comments
Open

JMM, JVM, Java ClassLoader #27

yunshuipiao opened this issue May 20, 2019 · 0 comments
Labels

Comments

@yunshuipiao
Copy link
Owner

yunshuipiao commented May 20, 2019

JMM, JVM, Java ClassLoader

JVM 内存结构

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

QQ20180624-150918

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

Java 内存模型

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

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

11

内存间相互操作

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

  • lock:作用于主内存变量,把一个变量标识为一条线程独占的状态
  • unlock: 主内存变量,释放处于锁定状态的变量,然后才可以被其他线程锁定
  • read:主内存变量,把一个变量的值从主内存传输到线程的工作内存,供 load 使用。
  • load:工作内存变量,把 read 操作从主内存得到的值放入工作内存的变量副本中
  • use:工作内存变量:将变量值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时,会执行这个操作。
  • assign:工作内存变量,把一个从执行殷勤接收到的值赋给工作内存的变量。
  • store:作用于工作内存的变量,把工作内存中一个变量的值传送到主内存中,供 write 使用。
  • write:主用于主内存变量,把 store 操作从工作内存中得到的变量的值放回主内存的变量中。

Java 对象模型

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

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

20170615230126453

类加载的过程

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

  • 遇到 new,get static, putstatic 或者 invokestatic 指令时,如果类没有初始化,则必须进行初始化。常见情况是,new 关键字实例化对象,读取或者设置一个类的静态字段(被final 修饰,在编译期把结果放入常量池除外),调用类的静态方法的时候。
  • 使用 java.lang.reflect 包的方法对类进行反射调用,没有初始化则进行初始化。
  • 初始化一个类,如果父类没有初始化则进行初始化。
  • 虚拟机启动,用户需要指定一个要执行的主类(包含 mian() 方法的那个类),虚拟机会先初始化该类。

具体过程

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

类加载器

双亲委派模型

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

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

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

总结

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

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

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

@yunshuipiao yunshuipiao changed the title JMM JMM, JVM, Java Class Model May 20, 2019
@yunshuipiao yunshuipiao removed the TODO label May 20, 2019
@yunshuipiao yunshuipiao changed the title JMM, JVM, Java Class Model JMM, JVM, Java ClassLoader May 27, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant