下面是按jvm虚拟机知识点分章节总结的一些jvm学习与面试相关的一些东西。一般作为Java程序员在面试的时候一般会问的大多就是Java内存区域、虚拟机垃圾算法、虚拟垃圾收集器、JVM内存管理这些问题了。
下边附面试常考问题,并且给出答案
深入理解虚拟机之Java内存区域:
1.介绍下Java内存区域(运行时数据区)。
2.对象的访问定位的两种方式。
深入理解虚拟机之垃圾回收
1.如何判断对象是否死亡(两种方法)。
2.简单的介绍一下强引用、软引用、弱引用、虚引用(虚引用与软引用和弱引用的区别、使用软引用能带来的好处)。
3.垃圾收集有哪些算法,各自的特点?
4.HotSpot为什么要分为新生代和老年代?
5.常见的垃圾回收器有那些?
6.介绍一下CMS,G1收集器。
7.Minor Gc和Full GC 有什么不同呢?
虚拟机性能监控和故障处理工具
1.JVM调优的常见命令行工具有哪些?
深入理解虚拟机之类文件结构
1.简单介绍一下Class类文件结构(常量池主要存放的是那两大常量?Class文件的继承关系是如何确定的?字段表、方法表、属性表主要包含那些信息?)
深入理解虚拟机之虚拟机类加载机制
1.简单说说类加载过程,里面执行了哪些操作?
2.对类加载器有了解吗?
3.什么是双亲委派模型?
4.双亲委派模型的工作过程以及使用它的好处。
Java面试通关手册(Java学习指南)github地址(欢迎star和pull):https://github.com/Snailclimb/Java_Guide (GITHUB 五万多星)
一、深入理解虚拟机之Java内存区域:
1.介绍下Java内存区域(运行时数据区)。
包括五大部分:方法区、堆、程序计数器 、本地方法栈、虚拟机栈
程序计数器
程序计数器是一个记录着当前线程所执行的字节码的行号指示器。
线程私有。字节码解释器工作是就是通过改变这个计数器的值来选取下一条需要执行指令的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖计数器完成
JAVA代码编译后的字节码在未经过JIT(实时编译器)编译前,其执行方式是通过“字节码解释器”进行解释执行。简单的工作原理为解释器读取装载入内存的字节码,按照顺序读取字节码指令。读取一个指令后,将该指令“翻译”成固定的操作,并根据这些操作进行分支、循环、跳转等流程。
从上面的描述中,可能会产生程序计数器是否是多余的疑问。因为沿着指令的顺序执行下去,即使是分支跳转这样的流程,跳转到指定的指令处按顺序继续执行是完全能够保证程序的执行顺序的。假设程序永远只有一个线程,这个疑问没有任何问题,也就是说并不需要程序计数器。但实际上程序是通过多个线程协同合作执行的。
首先我们要搞清楚JVM的多线程实现方式。JVM的多线程是通过CPU时间片轮转(即线程轮流切换并分配处理器执行时间)算法来实现的。也就是说,某个线程在执行过程中可能会因为时间片耗尽而被挂起,而另一个线程获取到时间片开始执行。当被挂起的线程重新获取到时间片的时候,它要想从被挂起的地方继续执行,就必须知道它上次执行到哪个位置,在JVM中,通过程序计数器来记录某个线程的字节码执行位置。因此,程序计数器是具备线程隔离的特性,也就是说,每个线程工作时都有属于自己的独立计数器。
Java虚拟机栈
线程私有,生命周期和线程一致。描述的是 Java 方法执行的内存模型:每个方法在执行时都会床创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。
局部变量表:存放了编译期可知的各种基本类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型)和 returnAddress 类型(指向了一条字节码指令的地址)
StackOverflowError:线程请求的栈深度大于虚拟机所允许的深度。
OutOfMemoryError:如果虚拟机栈可以动态扩展,而扩展时无法申请到足够
本地方法栈
区别于 Java 虚拟机栈的是,Java 虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。也会有 StackOverflowError 和 OutOfMemoryError 异常。
Java 堆
对于绝大多数应用来说,这块区域是 JVM 所管理的内存中最大的一块。线程共享,主要是存放对象实例和数组。内部会划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)。可以位于物理上不连续的空间,但是逻辑上要连续。
方法区
属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
2.对象的访问定位的两种方式。
(A)、使用句柄
Java堆划分一块内存作为句柄池,reference中存储就是对象的句柄地址;
对象句柄包含两个地址: (1)、在堆中分配的对象实例数据的地址; (2)、这个对象类型数据地址; 如图所示:
优点:对象移动时(垃圾回收时常见的动作),reference不需要修改,只改变句柄中实例数据指针;
(B)、使用直接指针
reference中存储就是在堆中分配的对象实例数据的地址;
而对象实例数据中需要有这个对象类型数据的相关信息(前面文章讨论了HotSpot使用对象头来存储对象类型数据地址);
如图所示:
优点:对象访问时节省了一次指针定位的时间开销,速度更快;
由于对象访问非常频繁进行,所以能较好提升性能;