文章目录
- JVM
- 区域划分(运行时数据区域)
- 对象的组成
- 垃圾收集
- 类加载
JVM
区域划分(运行时数据区域)
- 程序计数器——指向下一行要执行的代码行号——唯一一个没有规定OOM的地方
- 栈:本地方法栈、虚拟机栈
- 方法区
- 堆——存对象的地方,“几乎”所有的对象实例都在这里分配内存
对象的组成
- 假如内存是规整的——指针碰撞——要整理
指针移动需要多大的空间,给线程分配多大的空间,问题是要整理,把用过的不再需要的整理起来 - 假如不是规整的——空闲列表——空闲碎片
会产生大量的空间碎片问题 - 解决 一种是CAS(乐观锁) 一种是TLAB(本地线程分配缓冲) TLAB是线程私有的小块内存区域 作用就是减少多线程环境下对象分配时的锁竞争
- 对象头
(1)Mark Word:存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等
(2)指向对象类型的指针
(3)如果是数组的话 还额外存储了数组长度 - 实例数据
对象真正存储的有效信息 - 对齐填充
对象起始地址必须是8字节的整数倍,没有对齐的就需要通过对齐填充来补全
- 直接指针
- 句柄
垃圾收集
- 先判生死
(1)脑门刻字法——引用计数法
两个对象互相引用着对方,导致它们的引用计数都不为零,引用计数算法也
就无法回收它们。
(2)平地长树法——可达性分析
通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”,如果某个对象到GC Roots间没有任何引用链相连,则证明此对象是不可能再被使用的。
在Java技术体系里面,固定可作为GC Roots的对象包括以下几种: - 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
- 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。
- 在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。
- 在本地方法栈中JNI(即通常所说的Native方法)引用的对象。
- Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。
- 所有被同步锁(synchronized关键字)持有的对象。
- 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。
- 再谈引用
- 什么时间回收
- 分代收集理论
- 强分代假说——活的越久的,就越倾向于活下去
- 弱分代假说——绝大多数对象都是朝生熄灭的
- 跨代引用假说——仅占极少数
- 收集算法
- 标记-清除
-
- 标记-复制
-
- 没有空间碎片
- 仅适用于收集效率高的场景
- 只有一半的有效空间
- 标记-整理
- 怎么样回收
- 并发的可达性分析
当且仅当以下两个条件同时满足时,会产生“对象消失”的问题,即原本应该是黑色的对象被误标为白色: - 赋值器插入了一条或多条从黑色对象到白色对象的新引用;
- 赋值器删除了全部从灰色对象到该白色对象的直接或间接引用。
- 增量更新——破坏第一个条件(CMS)
黑色对象一旦新插入了指向白色对象的引用之后,它就变回灰色对象了 - 原始快照——破坏第二个条件(G1)
无论引用关系删除与否,都会按照刚刚开始扫描那一刻的对象图快照来
进行搜索
- CMS
- G1
- 对象优先在Eden
- 大对象直接进老年代
- 长期存活的进入到老年代
- 动态年龄判定
- 空间分配担保
类加载
- 加载
- 通过一个类的全限定名来获取定义此类的二进制字节流。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
- 验证
确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求 - 准备
正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段 - 解析
Java虚拟机将常量池内的符号引用替换为直接引用的过程 - 初始化
类加载过程的最后一个步骤,Java虚拟机真正开始执行类中编写的Java程序代码,将主导权移交给应用程序。
- 双亲委派模型
- 启动类加载器
- 扩展类加载器
-
- 应用程序类加载器(系统类加载器)
-
- 破坏双亲委派模型