目录
首先需要了解一下为什么需要JVM,JVM诞生的初期是为了实现跨平台运行的功能,机器本身只能认得0和1两个字符,我们程序员自己编写的.java文件机器并不能识别,通过javac将.java文件编译为.class文件,为什么要编译?因为JVM只能识别.class文件,在经由JVM的编译形成机器能够识别的文件。
JVM的内存划分
其中堆和方法区是线程之间共享的,而其他部分则是每个线程独有的。
堆:是用来存储所有对象的实例以及数组的地方,占有JVM最大的空间。
方法区:存放类信息,常量以及静态变量的地方。
程序计数器:只占有很小的一片空间,用来存储当前线程执行字节码的地址。
本地方法栈:和栈很相似,带有native关键字,是虚拟机栈为虚拟机执行java代码提供服务的,是JVM内部的c++代码的调用栈。
JVM栈:java代码的调用栈,生命周期与线程相等。每个方法执行的时候都会建立一个栈帧,而栈帧中会存储局部变量,操作数栈,动态链接和出口。虚拟机栈存储一个又一个的栈帧。
局部变量:存储八个基本数据类型,对象引用类型以及returnAddress(returnAddress存储return之后的字节码地址)
操作数栈:就是用来进行操作的地方
动态链接:在此方法中调用其他方法,需要连接到其他方法的过程就叫动态链接,用于存储链接方法。
出口方法:存储return或异常处理的地方
JVM内存参数
-Xms:设置初始堆大小
-Xmx:设置最大堆大小
-Xmn:设置堆中年轻代大小
-XX:NewSize=n:设置年轻代初始大小
-XX:MaxNewSize=n:设置年轻代最大值
-XX:NewRatio=n:设置年轻代和年老代的比值
-Xss:设置每个线程的堆栈大小
-XX:ThreadStackSize=n:线程堆栈大小
-XX:PermSize=n:设置持久代大小
-XX:MaxTenuringThreshold=n:设置年轻代垃圾对象最大年龄
垃圾回收机制
内存管理
1.申请内存:实例对象时候
2.释放内存(时机不明确):代码当中不是用这个对象就可以申请释放了
3.内存泄漏:若长时间不进行释放内存,就会导致可以申请的资源越来越少,直到程序的崩溃
判别垃圾
引用计数法(Java不使用):创建一个对象的时候分配一个计数器,每有一个引用指向该对象的时候计数器就会+1,引用销毁时计数器就会-1,计数器为0时没有引用指向该对象就会判定为垃圾,进行回收。
优点:回收迅速
缺点:计数器进行操作时陷入线程安全问题,需要进行加锁,加锁意味着效率将会变低。
会发生循环引用的情况,导致对象无法被回收。就像下面这段代码:
static class T{
public T t = null;
}
public static void main(String[] args){
T t1 = new T();
T t2 = new T();
t1.t = t2;
t2.t = t1;
}
可达性算法:从GCRoot出发,不断地扫描对象之间的引用关系,能够到达的对象为“可达”,不能到达的对象为“不可达”。一旦某个对象不可达,这个对象所持有的引用指向的对象也将不可达。
GCRoot的选取:
1.栈上的局部变量表中的引用
2.常量池中的引用指向的对象
3.方法区中静态引用类型的属性
四种引用类型
强引用:即可以找到对象也可以决定对象的生死。
软引用:能找到对象,一定程度的决定对象的生死
弱引用:能找到对象,不能决定生死
虚引用:既不能找到对象也不能决定生死
垃圾回收算法
标记清除法:先标记出垃圾在清除垃圾
优点:简单,容易实现
缺点:可能产生很多的内存碎片
复制算法:将内存分为两份,标记垃圾,将未标记的部分复制到另一区域,将这块区域一并回收。
优点:很好地解决了内存碎片的问题
缺点:回收的垃圾越少越低效;内存利用率低,需要分出来一半的内存用于复制。
标记整理:类似于顺序表中的删除元素
优点:空间利用率高了,也没有了内存碎片
缺点:内存搬运频繁,效率不高
分代回收法:将回收的过程分为几个区域,每个区域采用不同的回收方式
1.新的对象将创建在伊甸区当中
2.虚拟机会周期行对新生代进行可达性算法判断是否存在垃圾,每撑过一轮GC那么这些对象年龄就会+1,大部分的对象都活不过伊甸区
3.撑过一轮之后,对象会进入幸存区当中,两个幸存区相互配合使用复制算法进行垃圾清除
4.在幸存区经过重重GC后年龄到达一定程度就会进入到老年代,老年代中GC的频率会大幅下降
四个专业术语
Particlial GC:进行部分区域的垃圾回收
Full GC:针对全部内存的垃圾回收
Minor GC:针对年轻代空间回收内存。
Major GC:针对新生代+老年代的垃圾回收
类加载
类加载的步骤
1.加载:找到.class文件,解析.class文件的格式,存储到内存当中
2.链接:类和类之间需要配合,就把依赖的类一并加载。
3.初始化:对类对象进行初始化(初始化静态成员,执行静态代码块)
4.笔试常考:初始化的顺序:父类静态成员,父类静态代码块,子类静态成员,子类静态代码块,父类非静态成员,父类非静态代码块,父类构造器,子类非静态成员,子类非静态代码块,子类构造器。
双亲委派模型
BootstrapClassLoader负责加载Java标准库中的类,如String,ArrayList等
ExtensionClassLoader负责加载JVM扩展的类
ApplicationClassLoader负责加载自定义的类
过程:先由ApplicationClassLoader进行查询,但并非直接从负责的目录中查找,而是先去询问ExtensionClassLoader,ExtensionClassLoader也去询问BootstrapClassLoader,如果没找到才会往下查找。避免先加载自定义的类与标准库中的类冲突导致标准库的类无法使用。