对象的创建
1.当虚拟机遇到一个new指定时,会先去检查这个指定的参数是否能在常量池中定位到一个类的符号引用。
2.检查这个符号引用代表的类是否已经被加载,解析和初始化,如果没有,那必须先执行响应的类加载过程。
3.在类加载检查功能通过后,为新生对象分配内存。
对象的内存布局
1.对象头:有两部分,第一部分为:对象自身的运行时数据:hash码,锁的状态标志灯,也就是Mark Word
第二部分:类型指针,对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例
2.实例数据
也就是对象真正存储的有效信息,就是各种类型的字段内容
3.对齐填充
不是必然存在的,因为HotSopt VM 自动内存管理系统要求对象起始地址必须是8字节的整数倍,对象头部分正好是8字节的整数倍,所以当实例数据没有对齐时,就需要通过对齐填充来补全了。
对象的访问定位
有两种方式:
1.句柄访问
Java堆中会分出一块内存来作为句柄池,栈中reference存储的就是对象的句柄地址,句柄中包含了对实例数据与类型数据的格子具体信息。
2.直接指针访问
reference中存储的直接就是对象地址
Java 内存局域
方法区:用户存储已被虚拟机加载的类信息、常量、静态常量、
包含常量池:用户存放编译器生成的各种字面量和符号引用
堆:是内存中最大的一块,唯一的内存就是存放实例对象,是GC主要工作的区域
虚拟机栈:就是Java方法执行的内存模型,在每个方法执行时都会创建一个栈帧,里面有局部变量表,操作数栈,动态链接,方法出口等信息,每一个方法从调用到完成,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程
本地方法栈:功能与虚拟机栈相似,只不过他管理的是Native方法
程序计数器:一块较小的内存,是当前程序所执行的字节码行号指示器,就决定下一条需要执行的字节码可以
内存模型
目的:屏蔽掉各种硬件和操作系统的差异,以实现java能在各种平台下都能达到一致的内存访问效果
主要目标:定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内从中取出变量这样的底层细节
截屏2020-08-04 上午9.44.53.png
类加载机制
定义:把描述类的数据从class文件加载到内存中,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型
类的生命周期:
加载-验证-准备-解析-初始化-使用-卸载
1.加载
1.1通过一个类的全限定名来获取定义此类的二进制字节流
1.2将这个字节流所代表的静态存储结构转换为方法区内的运行时数据结构
1.3在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种 数据的访问入口。
验证:
是连接阶段的第一步,目的是为了确保Class文件的字节流中包含的信息符合当前 虚拟机的要求,并且不会危害虚拟机自身的安全。
a.文件格式验证 验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理。
b.元数据验证
对类的元数据信息进行语义校验,是否不存在不符合Java语言规范的元数据信息
c.字节码验证
最复杂的一个阶段,主要目的是通过数据流和控制流分析,确定程序语义是合法 的,符合逻辑的。对类的方法体进行校验分析,保证被校验类的方法在运行时不会 做出危害虚拟机安全的事件。
d.符号引用验证 最后一个阶段的校验发生在虚拟机将符号引用转换为直接引用的时候,这个转换动
作将在连接的第三个阶段——解析阶段中发生。
准备:
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段。这些变量所使用的 内存都将在方法区中分配。只包括类变量。初始值“通常情况”下是数据类型的零 值。
“特殊情况”下,如果类字段的字段属性表中存在ConstantValue属性,那么在准备阶 段变量的值就会被初始化为ConstantValue属性所指定的值。
解析:
虚拟机将常量池内的符号引用替换为直接引用的过程。
“动态解析”的含义就是必须等到程序实际运行到这条指令的时候,解析动作才能进 行。相对的,其余可触发解析的指令都是“静态”的,可以在刚刚完成加载阶段,还 没有开始执行代码时就进行解析。
初始化
初始化阶段是执行类构造器 <clinit>() 方法的过程。
<clinit>() 方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句 块中的语句合并产生的。
<clinit>与类的构造函数不同,它不需要显示地调用父类构造器,虚拟机会 保证在子类的 方法执行之前,父类的 <clinit>() 方法已经执行完 毕。
类加载器分类
1.启动类加载器(Bootstrap ClassLoader),主要负责存放在\lib目录或 Xbootclasspath参数指定的路径中的类库加载到内存中,即负责加载Java的核心类
2.其他类加载器:继承自抽象类ClassLoader
3.扩展类加载器(Extension ClassLoader):负责加载\lib\ext目录或其他系统变量指定自定路径的类库,即负责加载Java扩展的核心类之外的类
4.应用程序类加载器(Application ClassLoader):负责加载用户类路径上的指定类库
双亲委派模型
双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会 自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所 有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它 的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去 加载该类。
代码实现
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
//检查该类是否已经加载过
Class c = findLoadedClass(name);
if (c == null) {
//如果该类没有加载,则进入该分支 long t0 = System.nanoTime();
try {
if (parent != null) { //当父类的加载器不为空,则通过父类的loadClass来加载
c = parent.loadClass(name, false); } else {
//当父类的加载器为空,则调用启动类加载器来加载该类
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
//非空父类的类加载器无法找到相应的类,则抛出异常
}
if (c == null) { //当父类加载器无法加载时,则调用findClass方法来加载该类 long t1 = System.nanoTime();
c = findClass(name); //用户可通过覆写该方法,来自定义
//用于统计类加载器相关的信息
sun.misc.PerfCounter.getParentDelegationTime().a ddTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment(
if (resolve) { //对类进行link操作
resolveClass(c);
}
return c;
}
}
垃圾收集算法
1.标记-清除算法
截屏2020-08-05 上午9.39.43.png
截屏2020-08-05 上午9.40.46.png
截屏2020-08-05 上午9.41.01.png
截屏2020-08-05 上午9.41.13.png
JVM 如何判断对象是否已死
1.引用计数法
当给对象添加一个引用时,计数器就加1,当有一个引用失效时,计数器就减1,当计数器为0时就表示对象不能被再使用了,缺点:不能解决对象间的互循环引用问题
2.可达性分析算法
判断对象是否存活与“引用”有关
1.强引用:
就是指在程序代码之中普遍存在的,就比如new对象这一类的引用,只要强引用还存在,垃圾收集器就永远不会回收掉被引用的对象
2.软引用
用来描述一些还有用但是非必须的对象,在系统要发生内存溢出异常之前,将会把这些对象列入范围,进行第二次回收
3.弱引用:用户描述非必须对象的,被弱引用关联的对象只能生存到下一次垃圾收集发生之前,无论内存是否够用,都会回收掉弱引用关联的对象
4.虚引用:不能通过一个虚引用来取得一个对象实例,使用虚引用的目的就是在这个对象呗收集器回收时得到一个系统通知
网友评论