面试题:
面试官问:静态成员变量、实例变量在JVM内存区域是怎么布局的?线程安全吗?
01
面试官心理
首先这道题面试官考察你的是变量在JVM的内存区域布局你清楚吗?
其次我们假设在多线程高并发场景下这几个变量有没有线程安全的问题?
比如静态成员变量,你认为多线程场景下对同一个静态变量值的修改,是线程安全的吗?
02
我们循序渐进的分析
首先看下这张手绘变量图Java版
首先:我们知道当我们在本地跑main方法进行单测的时候,主线程调用到main方法的时会在JVM虚拟机栈空间内创建一个栈帧数据结构。
栈帧(Stack Frame)是用来支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。
其次:这里有一个局部变量的引用a指向了A实例对象。这个A对象是被分配在堆内存空间的。还有Class对象也是被分配在堆空间的。
最后:还剩一个静态成员变量,看看它会被分配在哪个内存区域呢? 答案是方法区。
方法区:它主要存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。
03
线程安全
什么是线程安全问题:
当多个线程对同一个对象中的资源(实例变量、静态变量)进行操作时候,会出现值被更改、值不同步的情况,进而影响程序的执行流程。
1)类的实例变量线程安全吗?
实例变量:非static的变量。该变量在方法之外定义。
多线程场景图如下:
我们知道对象实例是被分配在堆上的,然而堆又是所有线程共享的一块内存区域。
同一份实例变量,如果被多个线程并发修改的时候就会出现线程安全的问题。
2)位于方法区的静态变量,因为方法区本身被所有线程共享而且变量也只有一份,所以在这里存放的值也是线程不安全的。
类的静态变量不依赖类特定的实例,被类的所有实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象
看下面这张静态变量示例图:
输出结果打印:线程1获取第二次number=1
上述输出结果可能会是1呢?
我们假设线程1第一次读取到number的值是1,第二次读取到的值是2,刚好要打印输出我们以为的2的时候,别的线程并发的把number值修改成了1。
于是线程1很有可能最终输出的number值就是1了。