面试者:JVM就是我们的虚拟机,然后JMM我记得是并发编程如何保证数据可见性跟有序性。
面试官:具体点呢?
面试者:诶......
我来给你讲下吧:
先说 JVM(Java虚拟机)
JVM在跑起来的时候,会把操作系统给它的内存,划分成几个关键区域,每个地方放不同的东西,并且针对这些区域进行清理维护:
堆 你 new 出来的所有对象和数组 ,几乎都放在这里。它是垃圾回收器主要打扫的区域 。这是最大、最重要的一块 ,所有线程共享。
还有像 方法区(JDK8后叫元空间) 也是所有线程共享 。这里存放加载的类信息、常量、静态变量这些"模板"性质的东西。
还有线程私有的 虚拟机栈 本地方法栈 程序计数器等等
简单总结JVM: 它是个具体的执行引擎和内存管理者,负责把Java代码跑起来,管着内存的"物理"划分------哪里放对象、哪里放局部变量、垃圾怎么清理。
但是有个问题。上面提到的"堆"是共享的,意味着所有线程都能去访问里面的对象。但现代计算机为了快,CPU有高速缓存,编译器和处理器还会优化指令顺序(指令重排序)。这就会导致三个核心问题:
可见性问题 、有序性问题、 原子性问题
JMM(Java Memory Model), Java 内存模型就是为了定义一套规则,来解决这些问题。
JMM规定所有变量都存储在主内存(可以粗略理解为堆)中,每个线程有自己的工作内存(涵盖了缓存、寄存器等)。线程不能直接读写主内存,只能操作自己工作内存中的副本,然后再同步回主内存。
关键在于它定义的 happens-before 规则和提供的关键字:
happens-before 规则 是判断操作是否"可见"的核心原则,就是如果A操作 happens-before B操作,那么A做的所有改动,对B来说都是一定可见的。常见规则包括:程序顺序规则、监视器锁规则、传递性规则等等
然后就是几个关键字:
synchronized:当你要进入同步块时,JMM要求你先清空工作内存,从主内存重新加载变量。退出时,必须把修改过的变量刷回主内存。这样,就同时保证了原子性、可见性和有序性。volatile:它保证两件事:1)每次读都从主内存读,每次写都立刻刷回主内存(保证可见性 )。2)禁止指令重排序(保证有序性 )。但它不保证原子性。final:被final修饰的字段,一旦在构造器中初始化完成,并且构造过程没有"this"引用逸出,那么其他线程就能看到它的正确值。
总结下
- JVM是"实现者"和"平台" 。它具体负责内存怎么划分(堆、栈...),代码怎么执行,垃圾怎么回收。它是一个运行时实例。
- JMM是"规范"和"契约" 。它定义了在多线程这个特定场景下,数据应该如何被访问和同步,以保证正确性。它是一套规则。
所以,JVM管的是"程序怎么跑",JMM管的是"多线程环境下,怎么跑才对"。