内存原型
虚拟机堆
所有的对象和数组都会分配到堆上
句柄池
内存缓冲池
方法区
存储已被虚拟机加载的类的信息,变量,静态变量,即时编译后代码产生的缓存
运行时常量池
String类型使用的比较多
虚拟机栈
java方法执行的内存的模型。每一个方法的调用过程,对应着栈帧从入栈到出栈的过程。
局部变量表,方法出口,动态链接,操作数栈
本地方法区
和虚拟机栈功能相似,主要服务与本地方法,类似于c方法,JNI
程序技术器
字节码解释器通过改变程序计数器的数字从而确定指示器下一步执行的指令
显示程序执行的行号
线程共享
线程独享
不需要内存回收,随线程结束自动回收
内存分配
指针碰撞:规整分配内存
空闲列表:按照空置分配需要
对象结构
对象创建
创建对象内存划分方式
1.指针碰撞
2.空闲列表
保证对象创建安全(创建对象操作频繁)
1.采用CAS配上失败
重试的方式
2.本地线程缓冲区(TLAB)
开启配置-XX:UseTLAB
设置缓冲区再eden
默认占比1%
设置占比-XX:TLABWasteTargetPercent
线程初始化,在堆上会创建一个TLAB,给当前线程使用,创建对象时
会当前TLAB的剩余容量>=需要分配内存,直接创建对象内存
会当前TLAB的剩余容量<需要分配内存如果剩
剩余容量>浪费阈值,直接在共享的Eden区进行分配,当前TLAB保留
如果小于,就会重新再申请一块TLAB,当前TLAB被舍弃。
对象内部分布
对象内部结构
对象头
mark word
hash,锁状态,偏移时间,GC分代年龄
指针类型
实例数据
各个类型定义的字段值
对齐填充(可选)
数据存储时8位数,用于补全位数
对象访问方式
句柄池
引用中存储稳定对象的句柄地址,当对象移动时,不需要直接修改用,修改句柄地址指向就可有
直接指针访问
速度快,效率高,消耗少
主流虚拟机使用
垃圾回收
对象存活判断
引用计数法
逻辑:在对象中添加一个引用计数器,引用被使用就会加一,不适用就会减一
优点:原理简单
缺点:难以解决循环引用问题
主流虚拟机没有使用
可达性分析算法
通过一系列的GCroots的起始节点向下寻找,寻找引用的路径称为引用链。如果某个对象没有和引用链相连就会被认为时不可达的,进行垃圾回收
可以作为GC roots的固定对象
虚拟机栈(栈帧本地变量表)的引用对象
局部变量
临时变量
参数
方法区
静态变量引用
常量引用
例如string的字符串常量池
本地方法区
jni中引用对象
虚拟机内部引用对象
常用异常引用
系统类加载器
被锁同步的对象引用
回收判断
对象至少被标记两次才会被真正的执行回收,拥有自救的机会
标记一次后进行筛选,如果对象没有继承finalize方法或者已经被调用过finalize方法,就不会被执行finalize。如果有必要执行会放到F_queue队列中继续宁垃圾回收
方法区垃圾回收
废弃的引用
该类所有的实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例。
·加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如
OSGi、JSP的重加载等,否则通常是很难达成的
该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方
法
废弃的变量
引用类型
强引用:我们默认的应用类型,Object o=new Object();只要引用关系存就不会被回收
软引用:关联一些还有用但是非必须的对象,内存溢出前会进行二次回收。
SoftReference类来实现软引用
弱引用:也是关联一些非必要的对象,但强度比软引用还要弱,只关联到下次垃圾回收
WeakReference类来实现弱引用。
虚引用:主要作用就是跟踪垃圾回收
PhantomReference类来实现虚引用。
垃圾收集算法
引用计数式垃圾收集
子主题 1
追踪式垃圾收集
子主题 1
分代收集理论
弱分代假说(Weak Generational Hypothesis):绝大多数对象都是朝生夕灭的。
强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消亡
算法
标记-清除算法
首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象
缺点
执行效率不稳定
空间碎片化
标记-复制算法
它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
缺点
如果内存中多数对象都是存活的,这种算法将会产生大量的内存间复制的开销,
是将可用内存缩小为了原来的一半
新生代中的对象有98%熬不过第一轮收集
标记-整理算法
其中的标记过程仍然与“标记-清除”算法一样,让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内
存
移动则内存回收时会更复杂,不移动则内存分配时会更复杂。
收集器
Serial收集器
单线程,不怎么用了,停顿时间长,效率低
ParNew收集器
是Serial收集器的多线程并行版本
Parallel Scavenge收集器
Parallel Scavenge的诸多特性从表面上看和ParNew非常相似
尽可能地缩短垃圾收集时用户线程的停顿时间,
Serial Old收集器
Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。
Parallel Old收集器
Parallel Scavenge收集器的老年代版本,支持多线程并发收集,基于标记-整理算法实现
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用集中在互联网网站或者基于浏览器的B/S系统的服务端上,这类应用通常都会较为关注服务的响应速度,希望系统停顿时间尽可能短
步骤
1)初始标记(CMS initial mark)
2)并发标记(CMS concurrent mark)
3)重新标记(CMS remark)
4)并发清除(CMS concurrent sweep)
Garbage First收集器(G1)
是垃圾收集器技术发展历史上的里程碑式的成果,它开创了收集器面向局部收集的设计思路和基于Region的内存布局形式。
基于Region的堆内存布局
每个Region的大小可以通过参数-XX:G1HeapRegionSize设
定,取值范围为1MB~32MB,且应为2的N次幂
G1不再坚持固定大小以及固定数量的分代区域划分,而是把连续的Java堆划分为多个大小相等的独立区域(Region),每一个Region都可以根据需要,扮演新生代的Eden空间、Survivor空间,或者老年代空间
Region中还有一类特殊的Humongous区域,专门用来存储大对象
回收步骤
初始标记(Initial Marking):仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS
指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配新对象。这个阶段需要停顿线程,但耗时很短,而且是借用进行Minor GC的时候同步完成的,所以G1收集器在这个阶段实际并没有额外的停顿。
并发标记(Concurrent Marking):从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。当对象图扫描完成以后,还要重新处理SATB记录下的在并发时有引用变动的对象
·最终标记(Final Marking):对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留
下来的最后那少量的SATB记录
筛选回收(Live Data Counting and Evacuation):负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧Region的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成的
G1无论是为了垃圾收集产生的内存占用(Footprint)还是程序运行时的额外执行负载
(Overload)都要比CMS要高
说,虽然G1和CMS都使用卡表来处理跨代指针,但G1的卡表实现更为复杂,而且
堆中每个Region,无论扮演的是新生代还是老年代角色,都必须有一份卡表,这导致G1的记忆集(和
其他内存消耗)可能会占整个堆容量的20%乃至更多的内存空间
低延迟垃圾收集器
Shenandoah
在OracleJDK 12中支持Shenandoah收集器
像是G1的下一代继承者,它们两者有着相似的堆内存布局
基于Region的堆内存布局
需要1∶1复制对象到新Region的话,就必须要有一半的空闲Region来完成收集
ZGC
java15
不设分代的,使用了读屏障、染色指针和内存多重映射等技术来实现可并发的标记-整理算法的,以低延迟为首要目标的一款垃圾收集器
有4TB的内存限制,不能支持32位平台,不能支持压缩指针
基于Region的堆内存布局,的大、中、小三类容量
小型Region(Small Region):容量固定为2MB,用于放置小于256KB的小对象
中型Region(Medium Region):容量固定为32MB,用于放置大于等于256KB但小于4MB的对象。
大型Region(Large Region):容量不固定,可以动态变化,但必须为2MB的整数倍,用于放置4MB或以上的大对象。
采用的染色指针技术
·染色指针可以使得一旦某个Region的存活对象被移走之后,这个Region立即就能够被释放和重用掉,而不必等待整个堆中所有指向该Region的引用都被修正后才能清理
,短暂停顿也只与GC Roots大小相关而与堆内存大小无关,因而同样实现了任何堆上停顿都小于十毫秒的目标
全程并发
类加载机制
class文件内部结构
Class文件是一组以8个字节为基础单位的二进制流
魔数
每个Class文件的头4个字节被称为魔数
作用是确定这个文件是否为一个能被虚拟机接受的Class文件
紧接着魔数的4个字节存储的是Class文件的版本号
类加载过程
加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的
虚拟机性能监控与故障处理工具
JVM参数及调优