java面试知识总结--jvm相关(模型部分)
目标
为啥面试总爱问jvm的内存结构呢?更有甚者,还直接jmm,拽英文啊?实际工作又很少用到,凭啥总问我会不会呢?不过你问了,这里我就先来答下,哈哈。
本节主要内容,了解jvm内存模型,类加载,线程模型
内存模型
我喜欢按图说话:

分析下:
- java文件,通过java源码编译器我被翻译层class形式
- 我变成class形式后,包含三部分
- 结构信息:我的版本、各部分数量大小
- 元数据:常量、方法名、类信息等
- 方法信息: 语句、表达式、字节码、值栈信息
这时候我就等待java进程工作时来找我了.
可以通过javap -c
看下内容
public class HelloWorld extends BaseResponseDTO {
private static final int _1M = 1024 * 1024;
private volatile Integer integer=2;
private Boolean t=true;
public static void main(String[] args) throws InterruptedException {
HelloWorld H=new HelloWorld();
H.integer=+1;
H.t=false;
}
}
对应的class文件内容如下
public class com.yeepay.power.common.aop.HelloWorld extends com.yeepay.power.common.dto.BaseResponseDTO {
public com.yeepay.power.common.aop.HelloWorld();
Code:
0: aload_0
1: invokespecial #1 // Method com/yeepay/power/common/dto/BaseResponseDTO."<init>":()V
4: aload_0
5: iconst_2
6: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
9: putfield #3 // Field integer:Ljava/lang/Integer;
12: aload_0
13: iconst_1
14: invokestatic #4 // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
17: putfield #5 // Field t:Ljava/lang/Boolean;
20: return
public static void main(java.lang.String[]) throws java.lang.InterruptedException;
Code:
0: new #6 // class com/yeepay/power/common/aop/HelloWorld
3: dup
4: invokespecial #7 // Method "<init>":()V
7: astore_1
8: aload_1
9: iconst_1
10: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: putfield #3 // Field integer:Ljava/lang/Integer;
16: aload_1
17: iconst_0
18: invokestatic #4 // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
21: putfield #5 // Field t:Ljava/lang/Boolean;
24: return
}
- java进行开始执行,首先bootstrap先加载java等核心jar包,然后是ExtensionClassLoder加载java需要的扩展包,最后appClassLoader来加载刚才生成的class文件
这里有个概念叫做双亲委派模型
双亲委派模型工作过程是:如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。
-
加载完所有class,我的服务也起来了,这个时候,有用户开始访问我了.这时候又涉及到了jmm模型问题,看我的图中可以看出,我所创建的类信息、对象数据等都存在jvm的主内存中(当然jdk8中元数据是放到系统内存中的),我在处理用户数据时,首先要申请一个线程,让一个线程来处理用户工作.
-
我就是那个线程,我的工作生活是这样子的(可参考文章https://mp.weixin.qq.com/s/dAHaWLiqjkuc8UpoeF6e5A,写的不错),我要工作,所以我向jvm申请了1m的工作空间(工作内存,可通过-xss配置),看下我的日常工作流:
线程工作流
哈哈,我是有家的孩子,我的寿命很长哦!
-
对于我的工作空间,是这样子的:
图片来源网络
线程的working memory只是cpu的寄存器和高速缓存的抽象描述.cpu在计算的时候,并不总是从内存读取数据,它的数据读取顺序优先级 是:寄存器-高速缓存-内存。线程耗费的是CPU,线程计算的时候,原始的数据来自内存,在计算过程中,有些数据可能被频繁读取,这些数据被存储在寄存器 和高速缓存中,当线程计算完后,这些缓存的数据在适当的时候应该写回内存。
所以java内存模型分为主内存,和工作内存。主内存是所有的线程所共享的,工作内存是每个线程自己有一个,不是共享的。
每条线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝。线程对变量的所有操作(读取、赋值),都必须在工作内存中进行,而不能直接读写主内存中的变量。不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成,线程、主内存、工作内存三者之间的交互关系如下图:

每个线程通过JLS定义的八个对主存的操作指令:lock,unlock,read,load,use,assign,store,write。这些行为是不可分解的原子操作
参考资料:
https://www.cnblogs.com/wade-luffy/p/6051384.html
https://www.cnblogs.com/wxd0108/p/5479442.html
https://www.cnblogs.com/chihirotan/p/6486436.html