jvm生命周期
1、jvm启动
由引导类加载器bootstrap class loader通过创建一个初始类来完成的,这个类是由虚拟机具体的实现指定的。
2、jvm执行
执行java程序
3、jvm退出
程序正常执行结束;
程序在执行过程中出现异常或错误;
操作系统出现错误;
某线程调用Runtime类中halt()或System类中exit()
内存结构图
结构简图:
类加载器
三种类加载器:
BootstrapClassLoader 引导类加载器
是用C/C++实现的,用来加载java的核心类库,没有父加载器,用来加载拓展类加载器,系统类加载器;
出于安全考虑,Bootstrap加载器只加载 java、javax、sun开头的类。
ExtClassLoader 拓展类加载器
派生于ClassLoader,父类加载器是BootstrapClassLoader;
从jre/lib/ext目录加载类库
AppClassLoader 系统类加载器
派生于ClassLoader,父加载器是ExtClassLoader;
加载环境变量和系统属性,默认为应用程序的加载类
类加载过程:
获取加载器的方法
1 | // 获取当前类加载器 |
双亲委派机制
如果一个类的加载器收到了加载类的请求,并不会马上加载,而是将请求向上委托给父加载器,直到到达引导类加载器;如果父加载器能完成加载类的委托,则成功返回,否则,子加载器才会尝试自己加载。
好处:
1、安全,避免用户自定义的类动态加载,替换了java的核心类如String
;
2、避免类的重复加载,JVM区分不同的类,不仅仅是根据类名,相同的类被不同的类加载器加载也是不同的两个类。
本地方法栈(Native Method Stack)
线程私有,native方法用来调用C/C++程序,在内存中专门开辟了一块区域处理标记为native的代码。
程序寄存器(Program Counter Register)
线程私有,存储指向下一条指令的地址。
方法区(Method Stack)
各个线程共享,它存储了每个类的结构信息:字段和方法数据、构造方法和普通方法的字节码内容、常量池(jdk1.8在堆中的元空间中)。
方法区是规范,不同虚拟机的实现不同。(类似于接口的不同实现)
虚拟机栈(JVM Stack)
主管程序的运行,在线程创建时创建,它的生命周期跟随线程的生命周期,线程结束时释放,对于栈来说不存在垃圾回收问题,是线程私有的。
8种基本数据类型,对象的引用变量,实例方法都是在栈内存中分配的。
栈帧中主要存储3类数据:
本地变量:输入、输出参数,方法中的变量
栈操作:记录进栈、出栈
栈帧数据:运行时数据、方法等
栈+堆+方法区的关系:
堆(Heap)
主管数据的存储,一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。
类加载器读取了类文件后,需要把类,方法,常、变量放入堆内存中,保存所有引用类型的真实信息。
逻辑上分为三个部分:新生区、养老区、元空间
物理上分为两个部分:新生区、养老区
垃圾回收过程
Eden区满了,触发第一次GC,称为Minor GC,将存活数据复制到From区,清空Eden区,
GC过程(复制 -> 清空 -> 互换),
当Eden区再次触发GC时,会扫描Eden区和From区,对两个区域进行垃圾回收,经过这次回收还存活的对象直接复制到To区,同时把这些数据的年龄加1,清空Eden区和From区,清空后的From区成为下次GC的To区,完成交换,如此交换15次,若数据还有存活则存入养老区。
养老区满了,开启Full GC,FGC多次,养老区也无法腾出空间了,抛出OOM
永久代
是一个常驻内存区域,用于存放jdk自身携带的Class,Interface元数据,即运行环境必需的类信息,存放的数据不会被垃圾回收器回收,关闭JVM才会释放。
GC判断对象可以被回收的两种方式
引用计数法:每个对象有一个引用计数属性,新增一个引用计数加一,减少一个引用计数减一,当计数为零时可以被回收。可能存在循环引用的问题,比如A对象引用B对象,而B对象中又引用了A对象,当他们都不再使用后,因为相互引用,引用计数=1,永远无法回收。
可达性分析:从GC Roots向下搜索,搜索的路径称为引用链。当一个对象到GC Roots没用任何引用链相连时,说明该对象可回收。
GC Roots的对象有:
- 虚拟机栈中的引用的对象
- 方法区中的静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中引用的对象