目录
Java虚拟机运行时数据区如图所示:
其中红色部分为所有线程共享的数据区,即所有线程都访问这个数据区。灰色的为线程隔离的数据区,即每个线程都会维护一个自己的这个数据区。Java堆、方法区对于一个进程来说他们各自是唯一的。Java程序运行起来之后就是对应一个进程,一个进程就对应了一个JVM实例,一个JVM实例中就有一个运行时数据区,一个运行时数据区只有一个Java堆、一个方法区,这个进程下的多个进程共享这个Java堆、方法区,每个线程各自拥有一套PC寄存器、虚拟机栈、本地方法栈。
1、程序计数器(也叫PC寄存器)
面试常问问题:
(1):使用PC寄存器存储字节码指令地址有什么用?/为什么使用PC寄存器记录当前线程执行地址呢?
(2):PC寄存器为什么会被设定为线程私有?
2、Java虚拟机栈
栈是运行时单位,堆是存储的单位。即:栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储的问题,即数据怎么放、放在哪里。
每个线程创建的时候都会创建一个Java虚拟机栈,是线程私有的,所以生命周期跟线程是一致的。其内部保存的是一个个的栈帧,一个栈帧就对应一个Java方法,栈帧是一个内存区块,是一个数据集,维系着方法执行过程中的各种数据信息。不同线程中所包含的栈帧是不允许相互引用的。Java虚拟机主管程序的运行,它保存方法的局部变量(八种数据类型、对象的引用地址)、部分结果,并参与方法的调用和返回。
对于栈来说不存在GC问题,但是会有OOM。
2.1虚拟机栈中常见异常和栈大小的设置方法
2.2栈帧的内部结构
局部变量表(本地变量表)
定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量,这些数据类型包括各类基本数据类型、对象引用以及returnAddree类型。局部变量表最基本的存储单元是slot。在局部变量表里,32位以内的类型只占用一个slot(包括returnAddress),64位的类型(long和double)占两个slot。
2.2Java虚拟机栈的五到面试题
1、固定的栈空间大小,可以通过设置-Xss设置栈的大小,空间不足:StackOverflowError;动态的栈空间:OOM,当不断地加栈帧,就会导致整个内存都不足,报错OOM。
2、不能保证。理论上只能让StackOverflowError出现晚一点。
3、不是越大越好。会挤占其他内存空间变小了
4、GC不会涉及虚拟机栈
5、何为线程安全?如果只有一个线程才可以操作此数据,则必是线程安全;如果有多个线程操作此数据则此数据是共享数据。如果不考虑同步机制,会存在线程安全问题。
具体问题具体分析:stringbuilder这个类本身是没有加上同步的,所以是线程不安全的(stringbuffer加了同步锁所以本身就是线程安全的)
例子1:这个时候s1的声明方式是线程安全的,因为只有一个线程会去操作此数据
例子2:sBuilder的操作过程:线程不安全:主线程去操作了s,新建了一个线程也去并发操作了s,而sBuilder本身没有线程同步机制,就不安全了
例子3:s1的操作:线程不安全的:因为返回值是s1,一返回出去就有可能被其他线程所调用
例子4:s1的操作是线程安全的:s1在内部生的在内部死了,在内部消亡了,类似于例子1
3、Java堆
在虚拟机栈中放的是对象的引用地址,对象实例是放在Java堆中
如果对象在Eden区,出生经过一次minorgc后仍然存活,并且被survivor容纳的话,将会被移动到survivor区中,并将对象年龄设为1.对现在survivor区中梅奥过一次minorgc,年龄就增加一岁,当他的年龄增加到一定程度(默认为15),就会晋升到老年代中
3.1TLAB
3.2堆空间参数的设置
3.3堆是分配对象存储的唯一选择吗?
判断是否发生逃逸:看new对象实体是否会在方法外被引用
4、方法区
4.1栈、堆、方法区交互关系
堆存放的是new的对象,方法区有一部分放的是类的信息(.class)
方法区放的是什么数据?放的是类型信息。
5、运行时常量池
class文件中的常量池:
运行时常量池:字节码文件中的常量池经过类加载器加载放到了方法区后,就是运行时常量池
6、运行时数据区常见面试题