Java虚拟机(一)类加载、内存区域、垃圾回收策略
JVM简介
虚拟机是一种抽象化的计算机,Java虚拟机屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
字节码文件
字节码文件也就是经过编译之后的class文件。
执行流程简介
执行一个class文件,JVM中的类装载子系统首先会将class文件装载进入的内存区域(运行时数据区),再由执行引擎执行程序。
类加载机制
指的是将类的class文件的二进制数据读入到内存中,并将其放入到运行时数据区中的方法区内,并在堆区中创建一个对应的class对象。
类加载过程
- 加载:通过IO读入字节码文件;
- 连接:执行校验、准备、解析的步骤;
- 初始化:对类的静态变量进行初始化成指定的值,以及执行静态代码块;
- 使用;
- 卸载
类的初始化和卸载
初始化
类的初始化,包括生成对象的初始化和类的静态块的实例化。
初始化触发的时机: 类被直接引用(主动引用)的时候。
主动引用
主动引用的情形有:
-
使用new关健字实例化对象
-
使用类的静态变量
-
使用类的静态方法
-
使用反射机制调用上述操作
-
程序入口 (调用main方法)
初始化顺序是:
静态块 -> 非静态块 -> 构造函数。
如果有父类,则初始化顺序是:
父类静态块 -> 子类静态块 -> 父类非静态块 -> 父类构造函数 -> 子类非静态块 -> 子类构造函数
被动引用
被动引用不会触发类的初始化。
被动引用的情形:
- 引用父类的静态字段,只会初始化父类,不会初始化子类。
- 引用类的常量,不会引起类的初始化。
卸载
在类使用完之后,满足下面的情形,会被卸载:
- 该类在堆中的所有实例都已被回收,即在堆中不存在该类的实例对象。
- 加载该类的classLoader已经被回收。
- 该类对应的Class对象没有任何地方可以被引用,通过反射访问不到该Class对象。
如果类满足卸载条件,JVM就在GC的时候,对类进行卸载,即在方法区清除类的信息。
类加载器的种类
- 启动类加载器:加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar、charsetsjar等;
- 扩展类加载器:加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包;
- 应用程序类加载器:加载ClassPath路径下的类包,主要就是加载自己写的那些类;
自定义加载器:加载用户自定义路径下的类包。
运行时内存区域
分为两大类:线程私有区域、线程共有区域
线程私有区域:
- 程序计数器:吉利字节码执行的行号;
- java虚拟机栈:执行方法的时候,方法形成栈帧进行压栈,方法执行完毕时,栈帧出栈;
栈帧:包含局部变量表、操作数栈、方法出口等; - 本地方法栈:调用native方法时产生的栈。
线程共有区域:
- 堆内存: 分配所有对象实例的地方,也是垃圾回收的主要区域,也是内存分配最大的区域;
- 元数据区(方法区):虚拟机啊加载的类信息,常量,静态变量;
- 直接内存:非虚拟机内存,物理机的直接内存。
内存分配和垃圾回收策略
堆内存划分为年轻代,老年代。年轻代划分为eden区和survivor区,survivor区分为from区和to区;
对象优先在Eden区分配
大多数情况下,对象在eden区分配,eden区没有足够内存时,触发minorGC/YoungGC;
大对象直接进入老年代
可通过配置-XX:PretenureSizeThreshold来设置直接进入老年代的阈值,这样可以避免大对象在Eden区创建,因为大对象比较大,会造成minorGC的效率下降。
长期存活的对象将进入老年代
虚拟机给每个对象一个年龄计数器,Eden区触发minorGC,存活对象复制到from区,此时对象年龄设为1,之后的每一次熬过一次minorGC,年龄都会加1,当年龄增加到一定程度(默认15,可通过XX:MaxTenuringThreshold=数字 参数可以设置对象在经过多少次minorGC后会被放入老年代)
minorGC后存活的对象Survivor区放不下
这种情况时,JVM会将部分对象放入老年代,部分对象放入survivor区
Eden区和Survivor区内存分配比例
默认8:1:1
对象动态年龄判断
survivor区的一批对象的总大小大于这块survivor区总大小的50%,大于等于这批对象中最大年龄的对象的对象,就能直接进入老年代。
这篇博客写到挺详细:
https://www.jianshu.com/p/989d3b06a49d
老年代空间分配担保机制
JVM如何判断对象是否存活
- 引用计数法: 当对象引用计数器为0时,即无其他对象引用,判定为死亡,可被回收,但是存在循环引用问题,导致对象一直存活
- 可达性分析法:从GC root根向下搜索直接或间接可达的对象,判定为存活