一、JMM内存结构
模型图如下:
(1)java线程内存模型是基于CPU缓存模型建立的,java线程内存模型是标准化的,屏蔽掉了底层不同计算机的区别,每个线程都有一块自己的工作内存,包括L1、L2、L3三层缓存
(2)共享变量存于主内存,线程使用共享变量,需要拷贝一份副本到自己的工作内存
(3)缓存行:每次访问一个数据,都会缓存一整行数据(64字节)
二、八大原子操作
1、JMM内存模型,规定了八大原子操作:
(1)read(读取):从主内存中读取数据
(2)load(载入):将主内存读取到的数据载入工作内存中
(3)use(使用):从工作内存中读取数据来计算
(4)assign(赋值):将计算好的值重新赋值到工作内存中
(5)store(存储):将工作内存中的数据写入主内存
(6)write(写入):将store过去的值,赋值给主内存中的变量
(7)lock(锁定):将主内存变量加锁,标识为线程独占状态
(8)unlock(解锁):将主内存变量解锁,解锁后其它线程可以锁定该变量
三、缓存一致性问题
1、总线加锁
(1)CPU从主内存读取数据到工作内存,会在总线对这个数据加锁
(2)在read操作之前加锁,write之后释放锁
(3)串行执行,性能太低
2、MESI缓存一致性协议
(1)多线程通过 总线嗅探机制 监听共享资源,如果数据被修改,则把自己工作内存的副本失效,重新从主内存读取
(2)在store前加锁,保证共享资源可以共享读
(3)缓存行存在,该协议就会存在,加volatile修饰,是保证通知的及时性
(4)@Contended:保证类的成员变量只在一个缓存行
四、类加载器
1、BootstrapClassLoader(启动类加载器)
(1)最顶层的加载类,基于C++实现,通常返回null
(2)用于加载JDK内部的核心类库、被-bootclasspath参数指定目录下的所有类
2、ExtensionClassLoader(扩展类加载器)
(1)负责加载 lib/ext目录下的类和jar包、被java.ext.dirs系统变量所指定目录下的所有类
3、AppClassLoader(应用程序类加载器)
(1)负责加载当前应用classpath下的所有类和jar包
4、自定义加载类
(1)继承 ClassLoader抽象类,重写对应的方法
loadClass():加载指定二进制名称的类,实现了双亲委派机制
findClass():根据类的二进制名称来查找类,默认实现是空方法
5、判定两个类是同一个类
(1)类的全名相同,且加载此类的类加载器一样
五、双亲委派模型
1、定义
(1)当一个类加载器接收到类加载请求时,它会先将请求转发给父类加载器
(2)父类加载器找不到,该类加载器才会尝试自己去加载(findClass())
(3)子类也无法加载该类时,抛出类不存在异常
2、要求
(1)除顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器
(2)收到类加载请求,先委派给父类的类加载器去加载 ==> 所有请求最终都会到达启动类加载器
3、好处
(1)保证了 Java 程序的稳定运行
(2)可以避免类的重复加载
(3)保证了 Java 的核心 API 不被篡改
4、打破双亲委派模型
(1)可以重写loadClass()
(2)Tomcat 自己实现ClassLoad
六、类加载的过程
1、加载
(1)类加载器通过全类名获取定义此类的二进制流,转换成方法区运行时数据结构,在内存中生成一个Class对象,作为方法区数据访问的入口
(2)由双亲委派机制决定使用哪个类加载器
2、连接
2.1、验证
(1)确保Class文件中的信息符合JVM规范,不会危害虚拟机本身
(2)文件格式验证、元数据验证、字节码验证、符号引用验证
2.2、准备
(1)正式为类变量分配内存,并设置类变量初始值的阶段
2.3、解析
(1)将常量池内的符号引用替换为直接引用
3、初始化
(1)执行初始化方法 clinit,执行类对应的字节码文件
4、使用
5、卸载
(1)对象被回收(GC)