一、jvm基础
1. java从编译到执行
1)xxx.java文件通过javac命令执行成xxx.class文件
2)xxx.class文件通过java命令和classLoader加载到内存。另外,java类库也会加载到内存中。
3)调用字节码解释器或者JIT(即时编译器)进行解释或编译
4)编译完成后,由执行引擎进行执行
5)执行引擎下面是操作系统和硬件
2. jvm是跨平台的
jvm与java无关,jvm只是一种规范
任何语言,只要能编译成class文件,都可以在jvm上运行
同一个源文件,在windows和Linux上都能执行,不用修改任何东西(C和C++做不到)
jvm是一台虚拟的计算机
3. jvm常见的实现
关于java收费
其实是hotspot8之后的版本需要收费(17之后又开始不收费了),但是hotspot提供了一个免费的版本,Open JDK。也可以用TaobaoVM
4. jvm、jre、jdk
jdk>jre>jvm
二、class文件
1. class文件格式
可以用sublime或者notepad查看16进制格式的class文件,还可以用idea的插件BinEd
javap xxx.class命令,可以查看反编译的内容
javap -v xxx.class命令,可以查看详细的class文件的格式内容
同idea的Show ByteCode
JBP可以修改二进制文件
JClassLib(idea插件)也可以查看class的
结果:
根据class的16进制字节码含义:
2. 类加载过程
1)Loading(把class文件加载到内存中)
2)Linking
2.1)Verification(校验是否符合标准)
2.2)Preparation(把class文件的静态变量赋默认值)
2.3)Resolution(把class文件中,常量池里面用到的符号引用转换成直接的内存地址)
3)Initializing(静态变量赋初始值)
3. 类加载器
Bootstrap类型的类加载器会返回null
4. 类加载过程(采用双亲委派机制)
有一个class文件需要被加载到内存时,
1. 调用自定义CustomClassLoader的Custom.loadClass方法,如果在内存中找到了,返回结果;
2. 如果没找到,找到父加载器AppClassLoader,再在内存中找,如果找到了,返回结果;
3. 如果没找到,找到父加载器ExtClassLoader,再在内存中找,如果找到了,返回结果;
4. 如果没找到,找到父加载器Bootstrap,再在内存中找,如果找到了,返回结果;
5. 如果没找到,反过去让ExtClassLoader找到class并加载,如果加载成功,返回结果
6. 如果没有加载成功,让AppClassLoader找class并加载,如果加载成功,返回结果
7. 如果没有加载成功,让CustomClassLoader找class并加载,如果加载成功,返回结果
8. 如果没有加载成功,抛异常ClassNotFoundExecption
***这些定义来自于Launcher类
5. 为什么要用双亲委派加载类
主要为了安全,还有资源浪费问题
比如说String类如果使用自定义的类加载器会有很大的安全隐患,可以随意修改,所以需要使用定义好的Bootstrap类加载器来加载;还有就是,上面如果已经定义好了,再加载一遍造成资源浪费
6. 自定义类加载器
继承ClassLoader,重写findClass方法
7. java是解释执行的还是编译执行的
混合模式的,既可以用解释器也可以用编译器
三、JMM
1. 存储器的层次结构
越往上,容量越小,速度越快
从硬盘上读取数据到cpu上执行:
1)在硬盘上有一个数a,首先被load到内存
2)cpu读的时候,尝试去高速缓存L1中找,如果在,直接给cpu;
3)如果没有,去L2中找;如果L2中有,load到L1中并交给cpu
以此类推
2. 数据一致性问题
因为L1和L2在cpu内部,当cpu1改变变量a的值为1,cpu2改变a的值为2,就会出现数据不一致的问题
解决方案:
1)总线锁,总线锁会锁住总线,使得其他cpu不能访问内存中其他地址,效率很低
2)一致性协议,MESI Cache 【并发编程】MESI--CPU缓存一致性协议 - 风动静泉 - 博客园
现代CPU的数据一致性实现 = 缓存锁(MESI)+ 总线锁
3. 缓存行(cache line)
读取内存中的某个数据时,会把它附近的一块数据都读取出来,那一块数据称为缓存行
目前多数是64字节
4. 伪共享
位于同一缓存行的两个不同的数据,被两个不同的CPU锁定,产生互相影响的伪共享问题
对缓存行进行对齐,可以解决伪共享的问题,提高效率,但是对浪费一定的空间
四、有序性
1. 乱序
CPU为了提高指令执行效率,会在一条指令执行过程中(比如去内存读取数据),去同时执行另一条指令,前提是,两条指令没有依赖关系
现代cpu的合并写技术对程序的影响 - 刘少东的博客 - 博客园
写操作也可以合并
volatile可以保证有序
有序性保障(硬件级别)
1)CPU内存屏障
2)intel lock汇编指令
2.如果保证特殊情况下不乱序
1)硬件内存屏障
2)JVM级别
JVM级别的只是一种规范,还是依赖于硬件层面实现
3. volatile实现有序
1)字节码层面
有一个ACC_VOLATILE标记
2)JVM层面
volatile内存区的读写都加屏障
3)OS和硬件层面
volatile与lock前缀指令_zhifeng687的博客-CSDN博客
4. synchronized实现同步
synchronized不可以保证有序性
1)字节码层面
在代码块前后加monitorenter和monitorexit指令
在方法上加ACC_SYNCHRONIZED标记
2)JVM层面
C、C++调用了操作系统提供的同步机制
3)OS和硬件层面
X86 : lock comxchg xxxx
Java使用字节码和汇编语言同步分析volatile,synchronized的底层实现_21aspnet的博客-CSDN博客
5.happens-before原则
不管如何重排序,单线程执行结果不会改变
五、对象的内存布局
1. 对象的创建过程
2. 对象在内存中的存储布局
3. 对象头markword具体包含什么
一个字节是8位,也就是8bit,4bit可以存储最大的2的4次方-1