JDK,开发工具,面向开发者;JRE运行环境,面向使用者
Java引入虚拟机,指的是机器和编译程序之间的一层抽象的虚拟的机器,用于将字节码文件(.class文件)解释为特定系统的机器码,实现了一次编译,处处运行,通过字节码的方式,拆分了编译和解释,执行效率更高,且无须重新编译即可跨平台运行。
Java内存划分
- 方法区
用于存储被JVM加载的类信息、常量、静态变量等数据 - 堆
所有对象在这里分配内存,也是垃圾收集器进行垃圾收集的最重要的内存区域 - 虚拟机栈
描述Java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧,用于存储局部变量、操作数、动态链接、方法出口等信息,每个方法从调用到执行完成就是一个栈帧在虚拟机栈中入栈到出栈的过程。 - 本地方法栈
和虚拟机栈工作类似,本地方法栈为Native方法服务 - 程序计数器
指示当前线程所执行的字节码的行号
垃圾回收
判断可回收
- 引用计数法 每个对象都有一个引用计数器,当被引用则计数器加1,释放一个引用计数器减1,当计数器为0时表示对象没有与之关联的引用,可以被回收
- 可达性分析 如果在GC roots和一个对象之间没有可达路径,则称该对象是不可达的,至少经过两次标记过程才能变为可回收对象。当对象不可达时,GC会判断该对象是否覆盖了finalize()方法,若没有则直接回收,若覆盖了finalize()方法,则执行结束后再次判断该对象是否可达,若不可达则可回收,若可达,则该对象“复活”
Java中的四种引用类型
- 强引用
最常见的引用,把一个对象赋给一个引用变量,这个引用变量就是强引用,不会给垃圾回收 - 软引用
当内存足够时不会被回收,内存空间不足时会被回收 - 弱引用
只要垃圾回收机制一运行,不管内存空间是否足够,都会被回收 - 虚引用
主要作用是跟踪对象被垃圾回收的状态,必须和引用队列联合使用
GC roots
凡是被常量、静态变量、全局变量、运行时方法中的变量直接引用的对象,原则上不能被GC释放。能够成为GC roots的有虚拟机栈(本地变量表)中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中Native方法引用的变量
垃圾回收算法
- 标记清除算法(Mark-Sweep)
标记阶段标记出所有需要回收的对象,清除阶段回收被标记的对象所占用的空间。该算法容易导致碎片化严重 - 复制算法(Copying)
按内存容量将内存划分为等大小的两块,每次只使用其中的一块,当这一块存满后将尚存活的对象复制到另一块上去,把已使用的内存清掉。实现简单,不易产生碎片,但内存减少了一半 - 标记整理算法(Mark-Compact)
标记后将存活对象移向一端,然后清除端边界外的对象 - 分代收集算法
根据对象存活的不同生命周期划分新生代和老年代,根据不同区域选择不同的算法,新生代一般采用复制算法,老年代采用标记清除算法。
堆内存
新生代(Minor GC)
用于存放新生的对象。由于每次垃圾回收都要回收大部分对象,要复制的操作比较少,因而采用复制算法。一般将新生代划分为为Eden区(新对象的出生地)和两个Survivor区。JVM每次只会使用Eden和其中一块Survivor区域为对象服务,另一块Survivor区域空闲。当进行回收时,将Eden和From中还存活的对象复制到To区域中然后清理Eden和From区域,之后使用的就是Eden和To区域,如此反复,将Survivor区域这些存活的对象年龄设置为1,以后每经历一次垃圾回收,就将对象的年龄加1,当对象的年龄达到某个值(默认是15),就会进入老年代。
老年代(Major GC)
存放生命周期长的对象。由于老年代每次只回收少量对象,因为采用标记清除算法。
触发GC
- 当有新对象生成,在出生地申请空间失败时,会触发Minor GC,对新生代进行垃圾回收
- 新生代对象进入老年代区域时内存空间不足时会触发Major GC
- 老年代、永久代被写满会触发整个堆进行回收整理
- 调用system.gc()
Java类加载机制
类的生命周期
类加载过程包含了加载、验证、准备、解析和初始化5个阶段
类加载器和双亲委派机制
双亲委派机制的好处
- 提高了安全性,避免用户自己编写的类替换Java的一些核心类
- 避免了类的重复加载,Java区分不同的类除了判断类名,还会判断加载路径,相同的class文件被不同的ClassLoader加载是不同的两个类