由于计算机的存储设备与处理器的运算能力之间有几个数量级的差距。所以现代计算机系统都不得不加入一层读写速度尽可能接近处理器运算速度的高速缓存(cache)来作为内存与处理器之间的缓冲:将运算需要使用到的数据复制到缓存中,让运算能快速进行,当运算结束后再从缓存同步回内存之中,这样处理器就无需等待缓慢的内存读写了。基于高速缓存的存储交互很好地解决了处理器与内存的速度矛盾,但是引入了一个新的问题:缓存一致性(Cache Coherence)。
Java内存模型中规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存(可以与前面的处理器高速缓存类比),线程的工作内存中保存了该线程使用到的变量到主内存副本拷贝,线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同线程之间无法直接访问对象工作内存中的变量,线程间变量值的传递均需要在主内存来完成。
1、原子性
:
八种基本类型都具有原子性的。Java内存模型只保证了基本读取和赋值是原子性操作,如果要实现更大范围操作的原子性,可以通过synchronized和Lock来实现。由于synchronized和Lock能够保证任一时刻只有一个线程执行该代码块,那么自然就不存在原子性问题了,从而保证了原子性。
2、可见性
:
一个线程修改了变量,其他线程可以立即知道,保证可见性的方法:
volatile
synchronized(unlock)
final(一旦初始化完成,其他线程就可见)
3、有序性
:
在本线程内,操作都是有序的;在线程外,操作都是无序的。在Java内存模型中,允许编译器和处理对执行进行重排序,但是重排序过程不会影响到单线程程序的执行,却影响到多线程并发执行的正确性。在Java里面,可以通过volatile关键字来保证一定的"有序性"。另外可以通过synchronized和Lock来保证有序性,很显然,synchronized和Lock保证每个时刻是有一个线程来执行同步代码,相当于让线程顺序执行同步代码,自然就保证了有序性。对于volatile,JMM(Java Memory Model)内存屏障插入策略:
在每个volatile写操作的前面插入一个StoreStroe屏障
在每个volatile写操作的后面插入一个StroeLoad屏障
在每个volatile读操作的后面插入一个LoadLoad屏障
在每个volatile读操作的后面插入一个LoadStore屏障
JVM运行时数据区:
线程私有的数据区
可以分为程序计数器、虚拟机栈、本地方法栈;
线程共有的数据区
可以分为Java堆、方法区。
线程私有数据区
程序计数器
(Program Counter Register),也称作PC寄存器。记录当前线程执行到的字节码的行号,字节码的解释器工作的时候就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。JVM的多线程是通过线程轮流切换并分配由处理器来实现的,对于我们来说的并行事实上一个处理器也只会执行一条线程中的指令。所以,为了保证各线程指令的安全顺利执行,每条线程都有独立的私有程序计数器。此区域不会产生内存溢出(OOM OutOfMemoryError)异常。
虚拟机栈
(Java Virtual Machine Stacks),也就是我们常常说的栈。Java栈中存放的是一个个栈帧。一个栈帧包含如:局部变量表、操作数栈、动态链接、方法出口等信息。并且是线程私有,生命周期与线程相同。JVM是基于栈的,所以每个方法从调用到执行结束,就对应一个栈帧在虚拟机栈中入栈和出栈的整个过程。如果线程请求的栈深入大于虚拟机所允许的深度,将抛出StackOverflowError异常。如果虚拟机栈可以动态扩展(大部分虚拟机允许动态扩展,也可以设置固定大小的虚拟机栈),但是无法申请到足够的内存,会抛出OutOfMemorError。
本地方法栈
(Native Method Stacks),本地方法栈与虚拟机栈所发挥的作用很相似,他们的区别在于虚拟机栈为执行Java代码方法服务,而本地方法栈为Native方法服务。与虚拟机栈一样,本地方法栈也会抛出和StackOverflowError和OutOfMemoryError异常。在JVM规范中,并没有对本地方法的具体实现方法以及数据结构做强制规定,虚拟机可以自由实现它。
线程共享数据区域
Java堆
(Java Heap),Java堆可以说是虚拟机中最大的一块内存了。它是所有线程共享的内存区域,几乎所有的实例对象都是在这块区域中存放。当然,随着JIT(just in time,及时编译技术) 编译器的发展,所有对象在"堆"上分配也变得不那么"绝对"了。同时Java堆也是垃圾收集器管理的主要区域。
方法区
(Method Area),方法区在JVM中也是一个非常重要的区域,在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及JIT编译器编译后的代码等。它与堆一样,是被线程共享的区域。在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用。在方法区还有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表现形式,在类和接口被加载到JVM后,对应的运行时常量池,在运行期间也可以将新的常量放入运行时常量池,比如String的intern方法。
小结:
我们要先设计一块区域用来保存上面说的Java对象——》Java堆
;也要设计出一块区域用来保存类信息、常量等——》方法区
;既然我们的程序要解析方法,我们是不是要保存这个方法的信息,一个方法内部可能很长,我们要知道目前执行的位置——》程序计数器
;因为方法有本地方法和Java方法之分,对应跟踪每个方法内部的信息——》本地方法栈和虚拟机栈
。
一个Java虚拟机实例在运行过程中有3个子系统来保障它的正常运行,分别是:
类加载系统
、执行引擎系统
、垃圾回收系统
引导类加载器(Bootstrap ClassLoader):当运行Java虚拟机时,这个类加载器被创建,它加载了一些基本的Java API,包括Object这个类。需要注意的是,这个类加载器不是用Java语言写的,而是用C/C++写的。
扩展类加载器(Extension ClassLoader):这个类加载器出了基本API之外的一些拓展类,包括一些与安全性能相关的类。
系统类加载器(System ClassLoader):它加载应用程序中的类,也就是在你的classpath中配置的类。
自定义类加载器(User-Defined ClassLoader):这是开发人员通过拓展ClassLoader类定义的自定义加载类,加载程序员定义的一些类。
JVM物理结构图.png类生命周期:加载(加载二进制字节流、生成方法区的数据结构、创建Class实例)、验证(文件格式验证、元数据验证、字节码验证、符号引用验证)、准备(为类的静态变量分配内存并将其初始化为默认值)、解析(虚拟机将常量池内的符号引用替换为直接引用)、初始化(执行类中定义的Java程序代码,即执行类构造器<clinit>,实例对象使用<init>)、使用和卸载7个阶段,其中验证、准备和解析这是三个部分统称为连接(linking)。
实例化类的四种途径:
调用new操作符;
调用Class或java.lang.reflect.Constructor对象的newInstance()方法;
调用任何现有对象的clone()方法;
通过java.io.ObjectInputStream类的getObject()进行反序列化。
Java中new一个对象的过程:
1、类加载过程
1.1、JVM会先去方法区中寻找有没有相应类的.class存在。如果有就直接使用;如果没有就把相关的.class加载到方法区。
1.2、在.class加载到方法区时,会分为两个部分加载:先加载非静态内容,再加载静态内容。
1.3、加载非静态内容:把.class中的所有非静态内容加载到方法区下的非静态区域内。
1.4、加载静态内容:
1、把.class中的静态内容加载到方法区下的静态区域内;
2、静态内容加载完成之后,对所有静态变量进行默认初始化;
3、所有静态变量默认初始化完成之后,再进行显示初始化;
4、当静态区域下的所有静态变量显示初始化后,执行静态代码块。
1.5、当静态区域下的静态代码块,执行完之后,整个类的加载就完成了。
2、对象创建过程
2.1、在堆内存中开辟一块空间。
2.2、给开辟空间分配一个地址。
2.3、把对象的所有非静态成员加载到所开辟的空间下。
2.4、所有非静态成员变量默认初始化完成之后,调用构造函数。
2.5、所有非静态变量默认初始化完成之后,调用构造函数。
2.6、在构造函数入栈时,分为两部分:先执行构造函数中的隐式三步,再执行构造函数中书写的代码。
2.6.1、隐式三步:
1、执行super语句;
2、对开辟空间下的所有非静态成员变量进行显式初始化;
3、执行构造代码块。
2.6.2、在隐式三步执行完之后,执行构造函数中书写的代码。
2.7、在整个构造函数执行完并弹栈后,把空间分配的地址赋值给一个引用对象。
Java虚拟机把描述类的数据从Class文件加载到内存中,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的加载机制。Class文件由该类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等,Java允许用户借用这个Class相关的元信息对象简介调用Class对象的功能,这里就是我们经常能见到的Class类。
转载自:
作者:隔壁老李头
链接:https://www.jianshu.com/p/a3fdf9d4cbaf
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
网友评论