1.java平台无关性
为什么JVM不直接将源码解析成字节码去执行:(ByteCode是JVM唯一能够识别的指令,JVM将ByteCode翻译成真正能够执行的机器码)
准备工作:每次执行都需要各种检查(过于复杂)
兼容性:也可以将别的语言解析成字节码去执行。
2.jVM如何加载.class文件
Java虚拟机:抽象化的计算机。
存在的原因:屏蔽底层操作系统的不同,并且减少基于原生语言开发的复杂性。
JVM是内存虚拟机。JVM有完善的硬件架构。如处理器,堆栈,寄存器,相应的指令系统等
JVM的架构如下:
3.java反射机制
Java反射机制是在运行状态中。对于任意一个类,都能够知道这个类的所有属性跟方法;对于任意一个对象,都能够调用它的任意方法跟属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
4.谈一谈ClassLoader
类从编译到执行的过程:
ClassLoader:
BootstarpLoader:C++编写,用于加载核心类库java.*
ExtClassLoader: java编写,加载扩展库javax.*用户可见
从本地路径下查询相关的文件,有的话就进行加载。并且不是一次性加载,是用到才会加载。 相关代码如下:
AppClassLoader: java编写,用于加载程序所在目录。用户可见。
用来加载classpath下面的内容的。
去路径下找我们刚刚编译好的class
用户自定义的ClassLoader: java编写,定制化加载
自定义ClassLoader的实现:
关键函数:
5.ClassLoader的双亲委派机制
自底向上检查类对应的ClassLoader是否已经加载。完成此步骤的代码如下:
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
自顶向下尝试加载类,在对应的目录下查找是否有对应的jar包(类比:Robot.class)代码如下:
c = findClass(name);
使用双亲机制的核心目的:避免多份同样的字节码的加载
6.类的装载过程
链接的过程:
校验:举例:检查class的格式是否准确
准备:为类变量分配存储空间并设置变量初始值,类变量随类型信息存放在方法池中。生命周期很长,使用不当很容易造成内存泄漏。类变量就是static变量,初始值指的是类变量的默认值,而不是实际要赋的值。
LoadClass和forname的区别:
同:都能在运行时候,对任意一个类,都能够知道该类的所有属性跟方法。对于任意一个类,都能够调用他的方法跟属性。比较它们之前,需要了解Java类装载的过程。
作用:
异:class.forname()得到的class是已经初始化。(初始化就代表已经完成第三步了)
Classloder.loadClass得到的class是还没有链接的。
例:数据库驱动的加载,需要使用Class.forname().因为需要执行代码块中的静态部分。进而生成Driver对象。
例:数据库驱动的加载,需要使用Class.forname().因为需要执行代码块中的静态部分。进而生成Driver对象。
Classloder.loadClass的实际应用场景:在SpringIOC中,资源加载器在获取要读入的资源的时候,即读取一些bean的配置信息的时候,如果是以classpath的方式加载,就要使用Classloder.loadClass来加载,这样做是因为SpringIOC的懒加载机制有关。SpringIOC为了加快加载速度,大量使用lazyLoading。
7.JVM内存模型:线程私有
就是JVM架构中的RuntimeDataArea。
线程角度:
程序计数器:
当前线程所执行的字节码的行号指示器(逻辑)
改变计数器的值来选取下一条需要执行的字节码
和线程是一对一的关系即线程私有
对于Java方法计数,如果是native方法,则计数器的值为undefined
不会发生内存泄露
Java虚拟机栈:栈是后进先出。
局部变量表为操作数栈提供数据支撑。
Iconst_0:表示将int数0压入操作数栈当中。
Istore_2:将操作数栈栈顶元素0pop出来,存储到局部变量表的第2个变量当中(从零开始计算)。Store=出栈。
Iload_0:表示将局部变量表的第0个变量压入操作数栈栈顶。Load=入栈。
Iload_1:表示将局部变量表第1个变量压入操作数栈栈顶。
Iadd:将操作数栈里的1和2弹出,通过2+1去执行运算,将得到的结果压入栈顶。
Istore_2:将操作数栈顶元素3pop出来,存储到局部变量表的第2个变量当中,替换掉0。
Iload_2:将局部变量表的第2个变量压入操作数栈栈顶。
Ireturn:将栈顶元素返回出去。
部分问题:
问题:递归为什么会导致Java.lang.StackOverflowError异常
递归过深,栈帧数超过虚拟机栈深度
解决思路:限制递归的次数或直接使用循环的方法去替换递归
问题:
本地方法栈:
8.内存模型之线程共享部分
元空间:把类的源数据放置在本地堆内存中。该区域在jdk7以前属于永久代。元空间和永久代都是用来存储class相关信息。包括class的method,field等。元空间和永久代均是方法区的实现。
Native-heap就是元空间。 元空间没有字符串常量池,被引入到了堆中。
Java堆:
9.java内存模型值常考题目
(1)JVM三大性能调优参数:
一般情况下256就够了。
(2)java内存模型堆和栈的区别:
Main会分配对应的虚拟机栈,本地栈,以及程序计数器。栈里面会存有String类型的引用参数,保存的是对应于堆中test字符串Object对应的地址引用。还存有本地变量hw保存堆中helloworld这个对象的地址引用。此外,还有局部变量a,保存的是1这个值。以及系统自带的lineNumber,用来记录代码的执行。