JVM结构
主要有三部分构成:
一,classload(类加载器)
二,运行时数据区
三,引擎
classload(类加载器)
Java是运行在Java的虚拟机(JVM)中的,但是它是怎么就运行在JVM中了呢?我们在IDE中编写的Java源代码被编译器编译成.class的字节码文件。然后由我们得ClassLoader负责将这些class类加载到JVM中去执行。
JVM中提供了三层的ClassLoader:
Bootstrap classLoader:
主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。
ExtClassLoader:
主要负责加载jre/lib/ext目录下的一些扩展的jar。
AppClassLoader:
主要负责加载应用程序的主函数类
全委派机制:
应用该类的加载器是那个,该类也使用该类加载器
双亲委派机制:
统统向上级类提交AppClassLoader(判断是否已经加载过,加载过的话不再加载)没载过提交到ExtClassLoader,ExtClassLoader(判断是否已经加载过,加载过的话不再加载)提交到顶层Bootstrap classLoader,然后看顶层的Bootstrap classLoader(判断是否已经加载过,加载过的话不再加载)能不能加载类,不能再向下看ExtClassLoader能不那个加载,如果还不能就看AppClassLoader能不能加载,一般是可以加载的,除非你把他删除了,就会报ClassNotFoundException异常
为什么要用双亲委派机制:
这种设计有个好处是,如果有人想替换系统级别的类:String.java。篡改它的实现,但是在这种机制下这些系统的类已经被Bootstrap classLoader加载过了,所以并不会再去加载,从一定程度上防止了危险代码的植入。
双亲委派机制是可破坏的,可以自己重写它:打破双亲委派机制则不仅要继承ClassLoader类,还要重写loadClass和findClass方法 。不过jdk1.2.版本后一般不推荐这么做
运行时数据区
一般常说的java内存模型代指这里(虽然不是)
主要分为5个区域:
一和二为公共共享区域,三,四,五为私有区域
一,堆
实例化的对象(new出来的对象),GC(常说的jvm调优就是主要指这里,用来监视full gc的次数)
二,方法区
类信息,常量,静态数据,JITJIT(just in time)即时编译器是sun公司采用了hotspot虚拟机取代其开发的classic vm之后引入的一项技术,目的在于提高java程序的性能,改变人们“java比C/C++慢很多”这一尴尬印象。
三,jvm虚拟栈
每一个类都有私有的栈,里面有栈针,存储局部变量,操作数栈等,相当于jmm的工作区间
四,本地方法栈
每一个类都有私有的由c/c++写的本地方法,每个累都可以单独去调用这些方便放入自己的方法区中
五,pc计数器
存储执行下一条指令的地址(除了此次不会出现内存溢出,其它对方都有可能出现溢出,但是主要的是堆中会出现)
JMM
JAVA在运行时的内存模型-------->JMM
主内存:内存。共享数据
工作内存:工作空间。私有信息
工作方式:
A.线程修改私有数据,直接在工作空间修改
B.线程修改共享数据,把数据复制到自己的工作空间,在工作空间中修改,修改完成后刷新回主内存中(引用的地址放在工作空间中,也就是虚拟栈,引用的实际对象放在主内存中也就是堆)
JVM内存模型和硬件内存模型的关系
数据的不一致性
由于JVM内存模型和硬件内存模型的关系(交叉)------------>导致数据的不一致性,和并发问题
解决方案:
A,总线加锁----------------->降低cpu吞吐量
B.缓存的一致协议(MESI)
当cpu在缓存中操作数据时,如果该数据是共享的数据,就会把这个在缓存的数据直接读到寄存器中,同时把缓存中的一个属性LINE置为无效(是一种硬件设置),这样其它的cpu的缓存中也有该数据时就会放弃该数据重新从内存中读取
volatile:在jvm模型上实现MESI协议
synchronized:编译时的moitorenter(进入监控加锁)和monitorexit(退出监控解锁)实现的
并发编程的三个重要特性
一,原子性
原子性即操作不可分割,例:
int i = 10 ;
为原子性
i++:
不为原子性,因为它实际上是由i=i+1完成的,分为两部,中间可以断开不影响结果,所以不是原子性
多个原子合并一起要使还有原子性:
A.synchronized 加锁
B.JUC Lock中的lock 锁
二,可见性
线程只能操作自己工作空间的数据,对其它线程工作空间的数据是看不到的
volatile可以保证可见性同时还可以保证有序性
三,有序性
在编译成过程中不一定是按照程序员写的代码的顺序编译的,它会优化,有编译重排,指令重排,例
int i=1;
Cat cat = new Cat();
int b = 2;
实际上代码编译的时候会重排,会把int b =2;调到new cat对象之前,主要是为了提高效率(单线程重排并不会影响结果,但是多线程的会)
引擎
无了解