- 程序计数器:当前线程所执行的字节码的行号指示器,Java方法,记录正在执行的虚拟机字节码指令的地址,native方法,计数器值为空,唯一一个没有规定OutOfMemoryError的区域;
- Java虚拟机栈:Java方法执行的内存模型,方法执行创建一个栈帧,存储:局部变量表、操作数栈、动态链接、方法出口等信息,方法调用到完成,对应栈帧在虚拟机栈中的入栈到出栈的过程;(StackOverflowError、OutOfMemoryError)
- 本地方法栈:执行native方法服务,(StackOverflowError、OutOfMemoryError)
- 堆:对象实例、数组分配内存的地方,新生代(1/3)(Eden空间,From Survivor空间,To Survivor空间,比例为 8:1:1)、老年代(2/3);(OutOfMemoryError)
-
方法区:类信息、常量、静态变量、运行时常量池;(OutOfMemoryError)
跟垃圾收集器是否有压缩整理功能相关;
- 堆内存规整:用指针碰撞(指针分界点,往空闲的移动),CAS配上重试方式保证更新原子性
- 堆内存不规则:空闲列表(记录哪些内存块可用)
- 对象头:
第一部分:存储对象自身的运行时数据、哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向锁ID、偏向时间戳(32、64bit),官方称为:Mark Word;
第二部分:类型指针,指向它的类元数据的指针,确定对象是哪个类的实例
第三部分:如果对象是一个数组,有一块记录数组长度的数据
- 实例数据
属性的字段内容
- 对齐填充:不是必然存在,起占位符的作用,为了使对象的大小是8字节的整数倍;
- 使用句柄:reference存储对象的句柄地址,稳定的句柄地址,句柄中包含了对象的实例数据和类型数据的地址信息,如果对象移动则只需改变句柄中实例数据的指针,而reference不用修改;
- 直接指针:reference存的对象的实例地址,少一次指针定位;
- 引用计数法(很难解决对象之间的互相循环引用)
- 可达性算法分析(GC roots),以被称为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,一个对象到GC Roots 没有引用链 则被认为不可用;(虚拟机栈栈帧中的本地变量表中引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象,本地方法栈中native方法引用的对象);
- 回收方法区:该类的所有实例已经被回收,加载该类的ClassLoader已经被回收,该类的Class对象没有任何地方被引用;
- 强引用:强引用还在,垃圾回收器不回收;
- 软引用:系统内存不足,垃圾回收时会回收;
- 弱引用:下次垃圾收集发生,会回收;
- 虚引用:对象被垃圾收集器回收时收到一个通知;
- 复制:代价,内存缩小为原来的一半;
- 标记-清除:效率问题,标记和清除两个过程的效率都不高;空间问题,标记清除之后,会产生大量空间碎片;
- 标记-整理:所有存活的对象移到一端;
- 分代收集:结合各个年代使用不同的收集算法;新生代:复制算法,老年代:标记整理或标记清除
新生代收集:
- Serial收集器:单线程,一条收集线程去收集,暂停工作线程(Stop the World),简单高效;(复制算法、多线程) (配合 SerialOld和CMS)
- ParNew收集器:Serial的多线程版本,使用多线程进行垃圾收集;(复制算法,多线程)(配合 SerialOld和CMS)
- ParallelScavenge收集器:关注吞吐量,吞吐量优先的收集器,可设置,最大垃圾收集停顿时间(复制算法,并行的多线程收集),动态调节合适的停顿时间和最大吞吐量,自适应调节策略;(配合 SerialOld和Parallel Old)
老年代收集:
- Serial Old:单线程、标记-整理
- parallel old:多线程版本-标记-整理
- CMS:最短回收停顿时间,注重响应速度,标记-清除,初始标记、并发标记、重新标记、并发清除;初始标记和重新标记需要Stop the world,对CPU资源敏感(线程数=(cpu+3)/ 4),浮动垃圾,边收集,用户还在运行产生新的垃圾只能下次收集,需要预留(用户和收集线程并发)空间,大量空间碎片产生(标记-清除算法)
- G1:并行与并发,分代收集(可以独立管理不用和其他收集器配合),空间整合(从整体看是标记-整理算法,局部两个Region之间是复制算法,不会有空间碎片),可预测的停顿,堆分为多个大小相等的独立区域,优先回收价值最大的Region;初始标记、并发标记、最终标记、筛选回收(可独立的)
- 优先Eden分配,不足 则 MinorGC,Eden存活+From之前的到To(足够),不足则分配担保进入老年代,不能fullGC;
- 大对象直接进入老年代(配置参数)
- 长期存活的对象进入老年代(默认15岁)
- 动态年龄判定:Survivor空间中相同年龄所有对象大小的综合大于Survivor空间的一半,年龄大于等于改年龄的对象直接进入老年代;
- 空间分配担保,为了保证MinorGC安全,先检查老年代连续空间是否大于新生代所有对象总空间,如果大于则安全,否则检查是否设置了运行担保失败,如果允许,则会检查老年代空间是否大于历次晋升到老年代对象的平均大小,大于则MinorGC,小于则FullGC;
类生命周期:加载、验证、准备、解析、初始化、使用、卸载
五种情况必须对类进行初始化:(对一个类进行主动引用)
- new、getstatic、putstatic、invokestatic 使用new关键字实例化对象,读取或者设置一个类的静态字段、调用一个类的静态方法
- 反射调用,如果没有对类进行初始化,需要触发初始化
- 当初始化一个类时,发现其父类还没进行初始化,先触发父类的初始化
- 虚拟机启动时,用户需要指定一个执行的主类(包含main方法的那个类),虚拟机会先初始化这个主类
- 动态语言支持
被动引用不初始化:
使用子类调用父类的静态字段,子类不初始化,父类初始化;
数组定义引用类,不会触发类的初始化;
引用定义的常量的类,不会触发类的初始化(静态字段字符串);
加载:通过类的全限定名,生成class对象
验证:文件格式验证、元数据验证、字节码验证、符号引用验证
准备:类变量分配内存并设置初始值
解析:常量池内的符号引用改为直接引用
2、类加载器与双亲委派模型- 启动类加载器:rt.jar
- 扩展类加载器:ext
- 应用程序类加载器:classpath中类库(用户类、第三方引用jar)
加载器 先去找父类的加载(从缓存中找)找到就返回,找不到,到顶部后看类路径中有没有,有则加载,没有则交给下一个子类加载器加载,直到最后么有找到则报错;
高效并发1、volatile特性
- 保证此变量对所有线程的可见性(运算并发不一定安全,非原子)
- 禁止指令重排序优化
2、线程状态
- 新建:创建后未启动的线程;
- 运行:可能正在执行或者等待CPU为他分配执行时间;
- 无限期等待:不会分配CPU执行时间,等待被其他线程显式的唤醒;
- 限期等待:一定时间后系统自动唤醒
- 阻塞:等待获取排他锁
- 结束:
等待和阻塞的区别:阻塞等待进入同步区域,等待获取一个排他锁(竞争锁),等待,等待一段时间或等待唤醒动作的发生;



