存储局部变量 表、操作数栈、动态连接、方法出口等信息
方法区存储着已被Java虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据
JDK7把永久代的字符串常量池、静态变量等移出
描述为堆逻辑的一部分
判断对象对应的类是否加载、链接、初始化
没有将执行类加载过程
为对象分配内存
指针碰撞(内存规整)
所有被使用过的内存都被放在一边,空闲的内存被放在另一边,中间放着 一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间方向挪动 一段与对象大小相等的距离
空闲列表(内存不规整)
虚拟机就必须维护一个列表,记录上哪些内存块是可 用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的 记录
处理并发安全问题
对分配内存空间的动作进行同步处理
CAS加失败重试保证操作的原子性
把分配的内存的动作按照线程划分在不同的空间中
即每个 线程在 Java 堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)
初始化分配到的空间
设置对象的对象头
对对象进行必要的设置:哪个类的实例、 如何才能找到类的元数据信息、对象的哈希码(实际上对象的哈希码会延后到真正调用 Object::hashCode()方法时才计算)、对象的 GC 分代年龄等信息。
执行init方法进行初始化
对象头
存储对象自身的运行时数据(Mark Word)
哈希码
GC分代年龄
锁状态标识
线程持有的锁
偏向线程ID
偏向时间戳
类型指针
对象指向它的类型元数据的指针,Java 虚拟机通过这个指针来确定该对象是哪个类的实例。
实例数据
是对象真正存储的有效信息
即我们在程序代码里面所定义的 各种类型的字段内容,无论是从父类继承下来的,还是在子类中定义的字段都必须记录 起来。
对齐填充
不是必然存在的,也没什么特别的含义,仅仅起到占位符的作用
句柄访问
reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自 具体的地址信息
好处:reference中存储稳定句柄地址,对象被移动(垃圾收集时移动对象很普遍)时只会改变句柄中实例数据指针即可,reference本身不需要被修改
直接指针是局部变量表中的引用,直接指向堆中的实例,在对象实例中有类型指针,指向的是方法区中的对象类型数据
好处:速度更快,节省了一次指针的开销
引用计数算法
优点:实现简单,垃圾对象便于辨识;判定效率高,回收没有延迟性。
缺点:(1)(空间开销)他需要单独的字段存储计数器,这样的做法增加了存储空间的开销。
(2)(时间开销)每次赋值都需要更新计数器,伴随着加法和减法操作,这增加了时间开销。
(3)(循环引用)引用计数器还有一个严重的问题,即无法处理循环引用的问题,这是一条致命的缺陷,导致在Java回收的垃圾回收器中没有使用这类算法。
可达性分析算法
优点:解决了不能循环引用的问题
缺点:多线程情况下会造成误报或者漏报
强引用
是指在程序代码之中普遍存在的引用赋值,即类 似“Object obj=new Object()”这种引用关系
软引用
用来描述一些还有用,但非必须的对象
弱引用
用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引 用关联的对象只能生存到下一次垃圾收集发生为止。
虚引用
为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时 收到一个系统通知
终结器引用
至少经过俩次标记过程:如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,它将会被第一次标记,随后进行筛选,筛选的条件是此对象是否有必要执行finalize()方法,假如没有覆盖或者已经被虚拟机调用过,那么会被视为“没有必要执行”
如果有必要执行finalize()方法,会放置在一个F-Queue队列之中,并在稍后由一条虚拟机自动建立的、低调度优先级的优先级的Finalizer线程去执行finalize()方法。
finalize()方法是对象逃 脱死亡命运的最后一次机会,稍后收集器将对 F-Queue 中的对象进行第二次小规模的标 记,如果对象要在 finalize()中成功拯救自己——**只要重新与引用链上的任何一个对象建 立关联即可,譬如把自己(this 关键字)赋值给某个类变量或者对象的成员变量,**那在 第二次标记时它将被移出“即将回收”的集合。
方法区主要回收:废弃的常量和不再使用的类型。
三个条件:
标记-清除算法
标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对 象,也可以反过来,标记存活的对象,统一回收所有未被标记的对象。
缺点:
标记复制算法(“半区复制”)
它将可用内存按容量划分为大小相等的两块,每次只使用 其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后 再把已使用过的内存空间一次清理掉。
优点:
缺点:
标记-整理算法
优点:解决了内存碎片化的问题
缺点:开销大
GC Roots主要在全局性的引用(常量或类静态属性)与执行上下文(栈帧的本地变量表)中
分代收集算法
增量算法
特点:串行垃圾收集器
优点:单线程收集效率高
对于内存资源受限的环境,额外内存消耗最小
对于单核处理器或处理器核心数较少的来说,没有线程交互的开销,单线程能做到最高效率
缺点:停顿时间长
特点:Serial的并行版本
特点:为达到一个可控制的吞吐量(吞吐量优先收集器)
优点:有较好的吞吐量
缺点:
Serial的老年代版本
Parallel Scavenge 收集器的老年代版本
基于标记清除算法实现的
特点:获取最短的停顿时间给用户较好的体验,并发低停顿收集器
无法处理浮动垃圾,可能导致失败导致STW的Full GC
初始标记–并发标记–重新标记–并发清除
缺点:对处理器资源比较敏感,并发阶段会导致应用程序变慢,总吞吐量降低
收集结束会产生大量的空间碎片
垃圾收集的过程会更长,减少了对用户程序的影响(已废弃)
优点:不会产生空间碎片,可以精确地控制停顿
初始标记–并发标记–最终标记–筛选回收
内存占用方面:G1的记忆集所占的内存更多
执行负载角度:G1垃圾回收实现的异步处理
九个阶段
魔数与Class文件的版本
常量池
访问标识
类索引、父类索引与接口索引集合
字段表集合
(加载过程)生命周期:
加载:
验证
文件格式验证:字节流是否符合Class文件格式规范
元数据验证:对字节码进行语义分析,保证符合java语言规范
字节码验证:通过数据流分析和控制 流分析,确定程序语义是合法的、符合逻辑的。
符号引用验证:校验行为发生在虚拟机将符号引用转化为直接引用的时候,是对类自身 以外(常量池中的各种符号引用)的各类信息进行匹配性校验
通俗来说就是,该类是 否缺少或者被禁止访问它依赖的某些外部类、方法、字段等资源。
准备:正式为类中定义的变量(即静态变量,被 static 修饰的变量)分配内存 并设置类变量初始值
解析: Java 虚拟机将常量池内的符号引用替换为直接引用
初始化:根据程序员通过程序编码制定的主观计划去初始化类变量和其他资源
使用
卸载
双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会 自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加 载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有 当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时, 子加载器才会尝试自己去完成加载
按照 loadClass()方法的逻辑,如果父类加载失败,会自动调用 自己的 findClass()方法来完成加 载
这样既不影响用户按照自己的意愿去加载类,又可以保证新写 出来的类加载器是符 合双亲委派规则的。
引入了上下文类加载器
这个类加载器可以通过 java.lang.Thread 类的 setContext-ClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继 承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应 用程序类加载器。
代码热替换、模块热部署
局部变量表:是一组变量值的存储空间,存放方法参数和方法内部定义的局部变量
以变量槽为最小单位
操作数栈
动态连接
方法返回地址
附加信息