以下内容来自《深入理解Java虚拟机》,整理如下:
1.JVM组成
1.1 虚拟机栈
虚拟机栈描述的是java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈等。每个方法从调用到执行完成的过程,对应着一个栈帧在虚拟机栈中入栈到出栈的过程。平时说的栈内存指的是虚拟机栈的局部变量表部分,存储的是基本数据类型、对象引用和returnAddress类型(指向了一条字节码指令的地址)。
1.2 本地方法栈
作用和虚拟机栈类似,区别在于虚拟机栈为虚拟机执行java方法即字节码服务,而本地方法栈为虚拟机执行native方法服务。
1.3 程序计数器
为了线程切换后恢复到正确的执行位置,每条线程都要有一个独立的程序计数器。如果线程正在执行的是一个java方法,计数器记录的是正在执行的虚拟机字节码指令的地址。如果正在执行的是native方法,这个计数器的值为空。
1.4 方法区
存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,也被称作永久代。
内存回收的主要目标是废弃常量和无用的类。
1.5 堆
在虚拟机启动时创建,唯一目的是存储对象实例,几乎所有的对象实例都在这里分配内存。这部分就是平时常说的“堆内存”。
1.1~1.3是线程私有模块(与线程的创建消亡同步),1.4~1.5是线程共享模块(在JVM启动时即创建)。
2.如何判断对象已死?
2.1 引用计数法
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器的值就加1;当引用失效时,计数器值就减1;任何时候计数器值为0的对象就是不可能再被使用的。
主流JVM不用这算法管理内存,因为它很难解决对象之间相互循环引用的问题。
2.2 可达性分析
从GC Roots出发,向下搜索,走过的路径叫做引用链;当一个对象到GC Roots没有任何引用链时,证明此对象是不可用的。只是死缓!要宣告一个对象死亡,至少要经历两次标记过程。
2.3 判断对象是否存活都与“引用”有关
强引用:①new出来的;②只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
软引用:在系统将要发生内存溢出异常之前,会把这些对象列进回收范围之中进行第二次回收。
弱引用:只能生存到下一次垃圾收集发生之前。
虚引用:不会对生存时间构成影响,也无法通过虚引用取得对象实例。为一个对象设置虚引用的作用是在这个对象被收集器回收时收到一个系统通知。
3.垃圾收集算法
3.1 复制
应用场景:适用于新生代,大量消亡,少量存活-少量复制成本。
实现过程:将内存分为相等的两块,只用其中一块,当一块的内存满时,将还存活的对象复制到另一块上,然后将这块的内存清空。
算法缺点:内存明显小了。
3.2 标记-清理
应用场景:适用于老年代,对象存活率高。
实现过程:标记出要回收的对象, 然后清理。
算法缺点:效率低,会产生大量内存碎片。
3.3 标记-整理
应用场景:适用于老年代,对象存活率高。
实现过程:标记出要回收的对象,所有存活的对象向一端移动,在移动的过程中清理可回收对象。
算法缺点:效率较高,不会产生碎片。