JVM是Java Virtual Machine的简称,意为Java虚拟机。JVM是Java运行的基础,也是实现一次执行到处编译的关键。JVM的主要工作是:首先将编译好的字节码文件(.clacc)通过类加载器(ClassLoader)加载到JVM内存中,然后在通过执行引擎将字节码编译底层系统指令交给CPU执行。
JVM的内存布局(运行时数据区)
JVM的内存由堆区、方法区、栈区、程序计数器组成,其中堆区和方法区线程共享的,而程序计数器和栈区是每个线程所私有的。
关于各区所存放的数据
堆区:里面存放new出来的对象;
方法区:里面存放类对象的信息、final常量、字符串、静态变量等 ;
程序计数器:里面存放内存地址,这个内存地址指的是下一个要执行的指令地址;
栈:里面存放局部变量;
关于一个变量在内存中的位置
成员变量在堆中、局部变量在栈上、静态变量在方法区
JVM类加载
类加载的过程
类加载流程图:
各个阶段所做的工作:
加载阶段:该阶段的工作是在特定的目录中把.class文件找到并读取到JVM内存中
连接中验证阶段:这一阶段的工作是为了确保.class文件包含java虚拟机规范的全部约束,并且这些不会信息不会危害虚拟机自身的安全;
连接中准备阶段:这一阶段的工作是为变量分配内存并设置变量的初始值;
连接中中的解析阶段:这一阶段是将常量池的符号引用替换为直接引用的过程,也就是初始化常量
初始化阶段:这一阶段是执行构造器方法的过程,也就是真正开始执行程序员编写代码的过程
关于static变量的初始化和static代码块的执行
关于static变量的初始化和static代码块的执行是在对象实例化之前,如果在继承体系下,有一个口诀:由父及子,有静先行
双亲委派模型
双亲委派模型就是一种类加载(Loading)过程,该模型中有三个类加载器:AppClassLoader、ExtClassLoader、BootStrapClassLoader,它们的关系存在父子关系:
当一个类加载器加载一个类的时候,这个类加载器会将这个加载任务传递给他的父类加载器完成,每一个类加载器都是这样的。当父类加载器反馈自己无法完成加载时,子类加载器才会尝试去加载。
关于双亲委派模型中每个类加载器负责加载类的范围
AppClassLoader负责加载程序员自己写的类;
ExtClassLoader负责加载JVM扩展库中的类;
BootStrapClassLoader负责加载标准库中的类;
双亲委派模型的优点
因为双亲委派模型是根据优先级来加载类的, AppClassLoader优先级最低,BootStrapClassLoader优先级最高,当要加载类的名字在多个目录中是,这种优先级就会避免类的重复加载;
JVM垃圾回收机制
JVM其实就是一个进程,我们知道要运行一个进程是要很多资源支持的,其中就包括内存资源,我们所得JVM垃圾回收,回收的就是内存,这里的内存主要是JVM内存区域中堆区当中的数据。
标记垃圾对象的方法
a)引用计数法:引用计数就是通过一个变量来保存当前对象被几个引用所指向。他通过给对象增加一个引用计数器,每当有一个地方引用它时,计数器就+1,当引用失效时,计数器就-1.当计数器为0 时就表示这个对象没有被引用,此时就可以被回收掉了。
引用计数法的优点:规则简单,实现方便,比较高效;
缺点:空间利用率低(比较浪费空间,尤其是针对大量的小对象)、存在循环引用问题(因为这个问题因此java中没有使用引用计数机制);
b)可达性:从一组初始位置出发,向下进行深度遍历,把所有能够访问到的对象标记成"可达",访问不到的对象救认为是垃圾。
垃圾回收算法
a)标记——清除法
标记——清除法分为标记阶段和清除阶段,首先标记出所需要回收的对象,在标记完成后统一回收所有被标记的对象;
这种方法的缺点是:标记和清除这两个过程效率不高,并且标记清除以后会存在大量的内存碎片。
b)复制算法
复制算法是将内存划分成大小相同的两块,每次只使用其中的一块。当这块内存需要进行垃圾回收时,会将次区域还存活的对象拷贝到另一块上面,然后再把已经使用过的内存区域一次清理掉。
这种方法的优点:不会产生内存碎片并且运行效率高;
缺点:可用的对象内存空间只有一半,如果要回收的对象比较少,复制的开销就很多大;
c)标记——整理法
标记——整理法标记过程和“标记——清除”过程一致,但是并不会将标记好的对象就地清除,而是将所有存活的对象整理到一端,待清除的对象整理到另一端,然后在清除。
这种方法的优点:能够有效的避免内存碎片,并且也能够提高空间利用率;
缺点:在搬运整理的过程中,开销比较大;
分代回收法
分代回收法是对上面三种算法优点的一个整合,在JVM中进行回收时要进行扫描对象,每扫描一次对象就会“增长一岁” 。因此就可以根据这个年龄来对对象进行分类。主要分为年轻代和老年代,其中年轻代中又有一个伊甸区(刚创建出来的对象就放在这里),两个存活区:
一个对象从加载到JVM,再被GC线程给清理掉,首先JVM会把编译好的字节码文件内容加载到方法区,然后再根据类信息在堆区中创建出对象。对象首先会被分配到年轻代堆区中年轻代的伊甸区,经过一次GC后,如果对象依然存活,就会进入幸存区。在后面每次GC中,如果对象一只存活,就会在幸存区来回拷贝,每拷贝一次年龄就+1。但年龄超过一定数字之后就会进入老年代,在后面某次如果被标记为垃圾对象就会被清除。