对象的创建:
给对象分配内存的方式
指针碰撞(GC策略如具有压缩整理的功能,能够将堆内存划分为已使用和空闲的内存的时候可以使用指针碰撞,否则的话使用空闲列表)
空闲列表()
给对象分配内存线程安全性问题
线程同步加锁(性能很低)
本地线程分配缓冲(每一个线程分配一个缓冲内存,这样的话,就不会存在同步访问同一个资源的问题)
初始化对象
对象的结构:
-
- header(对象头)
- 自身运行时数据
- 哈希值 GC分带年龄 锁状态标志 线程持有的锁 偏向线程ID 偏向时间戳 等
- 这部分数据的长度在32位和64位的虚拟机(暂 不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为“Mark Word”。
- 自身运行时数据
- header(对象头)
-
-
- 类型指针(对象指向它的类的元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例,但是并不是所有的虚拟机都会保留类型指针)
- 如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据
- InstanceDate(数据的实例,存储对象的有效信息)
- Hotspot的分配策略是相同宽度的字段会分配在一块
- Padding(对齐填充,填充数据,不一定存在)
- 相当于占位符(因为Hotspot要求对象的起始地址必须是8个字节的整数倍,所以如果对象的实例数据如果不是8个字节,则使用padding填充)
-
执行构造方法
对象访问定位:
使用句柄
直接指针(hotspot使用的是直接指针)
垃圾回收GC
如何判断对象为垃圾对象
- 引用计数法
在对象中(堆内存中)添加一个引用计数器,当有地方引用这个对象的时候,引用计数 器的值就+1,当引用失效的时候,计数器的值就-1
可在Java的Run Configurations中设置如下参数,即可在进行垃圾回收的时候就可以在控制台看见垃圾回收的日志信息
-verbose:gc
-xx:+PrintGCDetails
- 可达性分析法(目前的主流的垃圾回收器都使用该方法)
定义一个垃圾回收的跟节点(GCRoot),从这个root节点开始找,当一个对象对root节点没有任何的引用链(找的时候走过的链路)相连接的时候,就判断这个对象是垃圾对象
能够作为GCRoots的对象
- 虚拟机栈(栈帧中局部变量表)
- 方法区的类属性所引用的对象
- 方法区总常量所引用的对象
- 本地方法栈中所引用的对象
如何回收
- 回收策略
- 标记-清除算法
- 效率问题
- 空间问题(会产生许多不联系的内存空间)
- 复制算法
- 标记-清除算法
为了方便垃圾回收,将堆内存分区
-
-
- 新生代
- Eden 伊甸园
- survivor 存活期
- tenured Gen 老年代
- 老年代
- 新生代
- 标记-整理-清楚算法
- 主要针对老年代的垃圾回收(垃圾很少,可用对象很多)
- 分代收集算法
- 新生代使用复制算法
- 老年代使用标记-整理-清楚算法
-
- 垃圾回收器
- Serial (复制算法)
- 单线程垃圾收集器(其他线程会暂停)
- 适合桌面运用
- 历史比较悠久
- Parnew(复制算法)
- 多线程垃圾收集器(其他线程需要暂停)
- 新生代收集器
- parallel scavenger (复制算法)
- 多线程垃圾收集器(其他线程需要暂停)
- 新生代收集器
- 达到可控制的吞吐量(吞吐量=执行用户代码的时间/(执行用户代码的时间+垃圾回 收所占用的时间))
- -xx:MaxGCPauseMillis垃圾收集器最大停顿时间
- -xx:GCTimeRatio 吞吐量大小
- 范围(0,99)
- Cms (Conturrent Mark Sweep)
- 使用标记-清除算法实现
- 减少延迟,提高响应速度
- 工作流程
- 初始标记
- 并发标记
- 重新标记
- 并发清理
- 优点
- 并发收集
- 低停顿
- 缺点
- 占用大量的CPU资源
- 无法处理浮动垃圾
- 出现concurrent mode failure
- 内存空间碎片
- G1收集器(jdk1.9)
- 优势
- 并行与并发
- 分代收集
- 空间整合(标记-整理算法的优势)
- 可预测的停顿
- 工作流程
- 初始标记
- 并发标记
- 最终标记
- 筛选回收(remember set表)
- 优势
- Serial (复制算法)
何时回收
对象内存的分配
内存分配策略
- 优先分配到Eden
- 大对象直接分配到老年代
- 长期存活的对象分配到老年代
- 空间分配担保
- 动态对象年龄判断
对象优先在Eden上分配
-Xms256m -Xmx1024m 堆内存的初始大小和最大大小
-XX:PermSize是初始永久保存区域大小,-XX:MaxPermSize是最大永久保存区域大小
大对象直接进入老年代
-XX:PretenureSizeThreshold=3145728
参数设定超过对象超过多少时,直接分配到老年代中
长期存活对象将进入老年代
年龄计数器:age1+1+1(存活一次age+1,当达到15(默认)之后,就会直接进入老年代)
-XX:MaxTenuringThreshold=15(默认15,不一定是15次之后才会进入老年代)
空间分配担保
当新生代空间不够的时候,会向老年代借用
-XX:+HandlePromotionFailure +号是开启,默认是开启的,-号是关闭
逃逸分析与栈上分配
逃逸分析:分析对象的作用域。
如果一个对象的作用域只是在方法之中(例如局部变量),那么给该对象的内存分配到栈中,随着方法的出栈而释放内存。