学这个JVM还是挺抽象的,不理解的东西我尽量记忆了,毕竟刚接触两天,也没遇到过实际应用场景,所以学起来还是挺费劲的,明天再补完垃圾回收这块的知识点。U•ェ•*U
先补一下JVM运行时的栈帧结构。
线程调用一个方法的执行和退出意味着一个栈帧的入栈和出栈,栈顶的栈帧叫当前栈帧,对应一个线程需要执行的最新的方法。其内部主要包含局部变量表、操作数栈、方法返回地址、动态链接等信息。
局部变量表(Local Variables Table)
用来存放方法参数和方法内部定义的局部变量。编译期就能确定局部变量表的容量,所以方法运行期间容量大小不会改变。
容量大小以变量槽(Variable Slot)为单位,变量槽可以重用,这个局部变量使用完成了,它占用的内存就可以给别的变量使用了。
每个变量槽的大小是32位,所以64位的变量会用两个连续的槽,并且JVM不允许以任何方式访问其中的一个。
操作数栈
主要用于保存计算过程的中间结果,同时作为计算过程中变量的临时存储空间。
方法返回地址
方法正常退出:方法正常执行完成后,会将返回值返回给调用方法
方法异常退出:方法执行时发生了异常并且没有得到妥善处理,也会触发方法退出,并且不会有返回值返回给调用方。
方法正常退出,主调方法PC计数器的值就可以作为返回地址。方法的退出,就是栈帧出栈的过程。
动态链接
java源文件编译为字节码文件的过程中,所有的变量和方法会作为符号引用,保存在Class文件的常量池中。
当一个方法调用另一个方法时,就是通过常量池中指向方法的符号引用表示的。
动态链接就是能将符号引用转换成调用方法的直接引用。
比如我在main里写一个method_one()方法,编译过程中首先在常量池(Constant pool)创建该方法的符号引用,然后会执行invokestatic #2指令,这个指令就是通过常量池中的符号引用#2表示的,在运行时,动态链接会根据这个符号引用真正地调用这个方法。
垃圾回收Part1
四大引用
图解
先把黑马的图粘贴过来,众所周知四大天王有五位,四大引用有五个也是正常的。
HotSpot通过直接引用来访问java对象,根据引用强弱分为四种,强软弱虚。Strong、soft、weak、Phantom。
强引用
对象的一般状态都是强引用。
一个对象有强引用,GC绝不会回收。
public class test {
public static void main(String[] args) {
String s = "sm";
HashMap map = new HashMap();
}
}
软引用
用于对象缓存,如果一个对象只有软引用,那么当内存不足,抛出OOM之前会回收软引用对象。
用法示例:
public class GCAnalization {
private static final int _8MB = 8 * 1024 * 1024;
private static final int _4MB = 4*1024*1024;
public static void main(String[] args) {
ArrayList<SoftReference<byte[]>> list1 = new ArrayList<>();
for(int i = 0; i < 10; i++){
list1.add(new SoftReference<>(new byte[_4MB]));
}
for(int i = 0; i < 10; i++){
System.out.println(list1.get(i));
}
}
}
弱引用
用于对象缓存,弱引用更低级,只要发生GC,弱引用就会被回收。
虚引用
好像没啥用,为对象设置一个虚引用,该对象被回收时能收到系统通知,必须与引用队列关联使用。
判断对象是否能回收的算法
引用计数法
当对象被引用一次,加1,不被引用,减1。
但是可能出现循环引用,这时就会发生内存泄露。
可达性分析
通过一系列GC Root(根对象节点)对象作为起点,根据引用关系,向下搜索,能找到的对象就是可达的,表示不可回收。
什么对象能作为GC Root?
比如:
1、虚拟机栈中引用的对象
2、方法区中类静态属性引用的对象
3、本地方法栈的Native对象
等等。
垃圾回收算法
标记清除
找出所有存活的对象,对存活的对象进行标记,清除未被标记的对象。
速度快,但会产生内存碎片。
标记整理
找出所有对象,对存活的对象进行标记,将存活对象整理到一端(紧凑),然后清除未被标记的对象。
没有内存碎片,速度慢
复制
划分两个大小相同的区域,每次只用一块,这块用完了,就把存活的对象复制到另一块,然后清理内存空间。这样交替使用两块内存。
没有内存碎片,占用空间大。