在不同操作系统上安装与之对应的JDK后所虚拟出的一台计算机,该计算机上可以运行任意Java字节码文件,这也是Java实现跨平台的原因
类加载器
- 类的加载器本身也是一个类,其作用就是加载本地class字节码文件到方法区中去创建class对象
- 加载顺序:启动类加载器[ jre/lib/rt.jar ]- 扩展类加载器[ jre/lib/ext/*.jar ] - 应用程序类加载器[ classpath指定的所有jar ]
- 加载模式:双亲委派,避免类的重复加载
- 加载机制:类的加载是动态的,优先加载保证程序运行的基础类,其它类则是在需要时才加载
class对象
- Java中有两种对象:Class对象(一个类只有一个Class对象)和实例对象(一个类可以new出多个实例对象)
- 隐士加载:new、调用静态变量或方法、子类加载前会优先触发对父类的加载
- 显示加载:反射。运行期间动态获取类的元信息并调用对象的属性和方法
- 卸载条件:类加载器被回收、所有实例对象被回收、class对象没有被引用(当Class对象被释放时,静态变量也会被销毁,因为静态变量的生命周期取决于类的生命周期)
JVM内存模型
JVM虚拟机中定义中的一种规范,存储类加载器加载的字节码文件信息(类的元信息、静态变量、常量等)
JDK1.7对其实现的是永久代,保存在堆中
JDK1.8对其实现的是本地元空间(可以有效降低内存溢出的风险),类的元信息移动到了物理内存中,其它的仍保存在在堆内存中
随着线程的创建而创建,每执行到一个方法便会创建一个栈帧入栈,当方法执行完毕栈帧也就出栈了,所有方法执行完毕,线程也就被释放了
存储new关键字创建的实例对象,GC工作区域
新生代分为一个较大“Eden”区和两个较小的Survivor区【默认Eden和Survivor的比例是8:1:1】,每次使用Eden和其中一块Survivor。
回收时将Eden和Survivor中还存活的对象一次性复制到另外一个Survivor区上,最后清理掉Eden和刚才用过的Survivor区【每次可用整个新生代的90%, 只有一个survivor被浪费】
当Survivor空间不够时,需要依赖老年代进行分配担保并直接存入老年代
老年代的内存空间要比新生代的大很多,如果老年代的内存满了就会触发major gc(只清除老年代)或者full gc(清理整个堆空间,会挂起当前应用中的所有进程直到垃圾清理完毕)
垃圾收集算法
- 分代算法
根据对象存活周期的不同将内存划分为新生代和老年代,根据各个年代的特点采用最适当的收集算法。
新生代中的对象生命周期很短,采用复制算法进行垃圾收集只需要付出少量存活对象的复制成本就可以完成收集
老年代中的对象存活率较高,没有额外的空间对它进行分配担保,所以必须使用”标记-清理“和”标记-压缩“算法来进行回收
垃圾收集器
- CMS收集器(JDK1.8)
停顿时间短,采用“标记-清除”算法,垃圾回收后会产生空间碎片,需要通过【-XX:CMSFullGCsBeforeCompaction】参数进行优化,设置执行N次的Full GC之后对空间碎片进行依次压缩 - G1收集器(JDK1.9默认)
将堆划分为多个大小相等的独立区域,新生代和老年代不再是物理隔离,它们都是一部分不连续的区域集合