- 谈谈对JVM的理解
- JVM中,类加载器的理解
- JVM的内存模型和分区
- 什么是OOM,什么是栈溢出StackOverFlowError,怎么分析
- JVM常用调优参数有哪些
- 内存快照怎么抓取,怎么分析Dump文件
- GC算法有哪些
JVM的位置
JVM的体系结构
JVM调优基本在方法区和堆处理
类加载器
作用:加载.class文件
如果某类还未被加载到内存中,则JVM会通过加载、连接、初始化3个步骤来对该类进行初始化。
- 加载指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象。
- 连接阶段负责把类的二进制数据合并到JRE中。
- 初始化是为类的静态变量赋予正确的初始值。
双亲委派机制
如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,
如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,
如果父类加载器可以完成类加载任务,就成功返回,
倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载。
根类加载器←扩展加载器←用户加载器←自定义的类加载器
沙箱安全机制
将 Java 代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。
沙箱主要限制系统资源访问,系统资源包括CPU、内存、文件系统、网络。
不同级别的沙箱对这些资源访问的限制也可以不一样。
Native
使用Native关键字说明java作用范围达不到,需要调用底层c语言的库。
进入本地方法栈,调用本地方法接口(JNI)
JNI作用:扩展java的使用,融合不同的编程语言为java所用
PC寄存器
PC寄存器是线程私有的指针,用来存储指向下一条指令的地址,也就是即将要执行的指令代码。由执行引擎读取下一条指令。
-
它是一块很小的内存空间,几乎可以忽略不计。也是运行速度最快的存储区域
-
在jvm规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期保持一致
-
任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的java方法的JVM指令地址;如果实在执行native方法,则是未指定值(undefined),因为程序计数器不负责本地方法栈。
-
它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成
-
字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令
-
它是唯一一个在java虚拟机规范中没有规定任何OOM(Out Of Memery)情况的区域,而且没有垃圾回收
1.使用PC寄存器存储字节码指令地址有什么用呢(为什么使用PC寄存器记录当前线程的执行地址呢)
(1)多线程宏观上是并行(多个事件在同一时刻同时发生)的,但实际上是并发交替执行的
(2)因为CPU需要不停的切换各个线程,这时候切换回来以后,就得知道接着从哪开始继续执行
(3)JVM的字节码解释器就需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令
所以,众多线程在并发执行过程中,任何一个确定的时刻,一个处理器或者多核处理器中的一个内核,只会执行某个线程中的一条指令。这样必然导致经常中断或恢复,如何保证分毫无差呢?每个线程在创建后,都会产生自己的程序计数器和栈帧,程序计数器在各个线程之间互不影响。
2.PC寄存器为什么会设定为线程私有?
(1)我们都知道所谓的多线程在一个特定的时间段内只会执行其中某一个线程的方法,CPU会不停滴做任务切换,这样必然会导致经常中断或恢复,如何保证分毫无差呢?
(2)为了能够准确地记录各个线程正在执行的当前字节码指令地址,最好的办法自然是为每一个线程都分配一个PC寄存器,这样一来各个线程之间便可以进行独立计算,从而不会出现相互干扰的情况。
方法区
线程共享的内存区域,存储已被虚拟机加载的类信息(构造方法,接口)、常量、静态变量,静态代码块、常量池,即时编译器(JIT Compiler)编译后的代码数据等。
这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。
栈
栈内存,主管程序运行,生命周期和线程同步。但资源占满就栈溢出。线程结束释放栈内存,不存在垃圾回收。
栈溢出是指向栈中写入了超出限定长度的数据,溢出的数据会覆盖栈中其它数据,从而影响程序的运行。