jvm 内存管理
************************
运行时数据区
程序计数器:线程执行代码的行号指示器
如果是java方法,记录的是虚拟机正在执行的字节指令代码地址;
如果是native方法,程序计数器为空
程序计数器不会发生内存空间不足
虚拟机栈:存储局部变量表、操作数栈、动态连接、方法返回地址
如果线程请求栈的深度大于最大深度,抛出StackOverflowError异常;
如果线程无法申请足够的桟空间,抛出OutOfMemoryError异常
本地方法栈:与虚拟机栈类似,虚拟机栈操作java方法,本地方法栈操作native方法
也会抛出StackOverflowError、OutOfMemoryError异常,有的虚拟机将虚拟机栈和本地方法栈合并使用
堆:分配内存空间供对象使用,内存不足会抛出outOfMemoryError异常
方法区:存储类信息、常量、静态变量、即时编译器编译后的代码等信息
使用永久代实现方法区容易造成内存溢出,虚拟机改用元空间(native memory)实现方法区
运行时常量池是方法区的一部分,存储编译期生成的字面量和符号引用
直接内存不是运行时数据区的一部分,但也可能会导致OutOfMemoryError,用native函数分配堆外内存,并在堆中开辟内存空间作为直接内存的引用
************************
对象创建过程
判断类信息是否加载:如果没有加载先加载类信息到虚拟机内存;
为对象分配内存空间:指针碰撞(内存空间连续)、空闲列表(内存空间不连续);
初始化对象:为对象设置默认值,静态变量设自定义值,执行静态语句块
设置对象头:hashcode、年龄代、锁信息等
调用实例构造器实例化对象:设置用户自定义的值
对象访问方式:句柄池、直接指针
************************
垃圾回收
判断对象存活:可达性分析算法
可达性分析算法:如果对象通过引用链与GCRoot直接或者间接相连,则对象存活,否则标记对象可以被回收
枚举GCRoot:
虚拟机会在类加载会使用特定的数据结构(OopMap)记录对象的引用关系,
jit编译的时候也会在安全点或者安全区域记录GCRoot的位置,
不需要遍历整个栈、方法区的内存空间,可以在垃圾回收的时候快速找到GCRoot的位置
安全点:线程长时间执行的位置,确保对象的引用关系在此期间不会发生变化,如方法调用处、循环跳转、异常跳转处等
安全区域:可看作安全点的拓展,如线程休眠、线程阻塞,线程进入安全区域时会标记自己进入安全区域,垃圾回收时可直接枚举根节点,不用管进入安全区域的线程,线程阻塞结束时线程会先进行判断枚举根节点是否结束,如果没有结束则继续等待
引用类型
强引用:Object object=new Object();
软引用:内存不足时回收对象
弱引用:垃圾回收时回收对象
虚引用:最弱的引用关系,对象回收时可收到系统通知
垃圾回收算法
标记清除算法:标记可回收对象,垃圾回收是直接清除对象
复制算法:标记可回收对象,垃圾回收时将存活对象整体复制到一块区间,清楚其他对象
整理算法:标记可回收对象,垃圾回收时将存活对象向一边移动,清除其余对象
堆内存划分:新生代、老年代
新生代空间划分为1个eden,2个survivor,默认比例8:1,其中一个survivor为空,垃圾回收时,将存活对象复制到空的survivor,清除其他空间,如果内存空间不足,对象进入老年代,老年代空间不足触发full gc
内存分配规则
对象优先分配在eden区;
大对象直接进老年代,相关参数:-XX:PretenureSizeThreshold;
长期存活对象进老年代,相关参数:-XX:MaxTenuringThreshold;
如果survivor空间相同年龄的对象占空间超过一半,则大于此年龄的对象进老年代;
空间分配担保:老年代连续空间大于新生代存活对象大小或者历次进入老年代平均大小,触发minor gc,否则进行full gc