JVM

JVM
JVM内存模型
- 程序计数器
- 虚拟机栈
- 本地方法栈
- 堆
- 元空间:存储加载的类信息、常量、静态变量、JIT编译后的代码缓存等
- 运行时常量池
Java堆
线程共享区域,主要用来保存对象和数组。- 年轻代
- 老年代
jdk1.7的堆区有一个永久代,存储的是类信息、静态变量、常量、JIT编译后的代码缓存等。
jdk1.8移除了永久代,改用本地内存区的元空间,避免内存溢出。
虚拟机栈
每个线程运行时所需要的内存。默认栈内存为1M。
方法内的局部变量是线程安全的。方法区
方法区是各个线程共享的内存区域,存储的是类信息、运行时常量池。
虚拟机启动时创建,关闭时释放。- 常量池:类名、方法名、参数类型、字面量等
- 运行时常量池:当类被加载时,常量池信息会被放入运行时常量池中,并将符号地址变为内存地址。
直接内存
不属于JVM的内存,不由JVM管理,是虚拟机的系统内存。
常见于NIO操作时,用于数据缓冲区。类加载器
将字节码文件加载到JVM中。- 启动类加载器:加载jre/lib下的库
- 扩展类加载器:加载jre/lib/ext下的类
- 应用程序类加载器:加载classpath下的类
- 自定义类加载器
双亲委派模型
- 加载类时,先委托上级加载器加载,如果找不到,则由子加载器加载。
- 避免类被重复加载,保证类库API不会被修改。
类装载的执行过程
- 加载:将字节码文件加载到JVM中。
- 验证:验证字节码文件的正确性。
- 准备:为类变量分配内存,并赋予初始值。
- 解析:将符号引用转换为直接引用。
- 初始化:执行类的静态代码块和静态变量的初始化。
- 使用:JVM从入口方法中开始执行。
- 卸载:当类不再被使用时,卸载掉。
对象创建的过程
- 类加载检查
- 分配内存
- 初始化零值
- 进行必要设置(对象头等)
- 执行构造方法
垃圾回收
- 引用计数法
- 可达性分析:沿
GC Root
对象为起点的引用链找不到的对象则可以被回收。- 虚拟机栈中引用的对象
- 类静态属性引用的对象
- 常量引用的对象
- 本地方法栈中引用的对象
垃圾回收算法
- 标记清除算法
- 标记存活对象,清除无用对象。
- 效率高,内存碎片多。
- 标记整理算法
- 对象需要移动,效率低
- 复制算法
- 两块内存,每次只使用其中一块。
- 内存利用率低
- 标记清除算法
分代回收
年轻代和老年代
- 年轻代:三分之一
- Eden:0.8
- from:0.1
- to:0.1
- 老年代:三分之二
工作机制
- 新创建的对象放到Eden中。
- Eden区内存不足时,标记Eden和from中的存活对象,将其移动到to,然后释放Eden和from区。
- 幸存对象经过多次回收(最多15次)后,移到老年代区。
- 幸存区内存不足或大对象,会提前移到老年代区。
GC
- MinorGC(youngGC):仅新生代,暂停时间短
- MixedGC:新生代和部分老年代,G1收集器特有
- FullGC:全部新生代和老年代,暂停时间长垃圾回收器
- 串行垃圾收集器:只有一个线程工作,适合堆内存小的情况
- Serial:采用复制算法,作用于新生代
- SerialOld:采用标记整理算法,作用于老年代
- 并行垃圾收集器(jdk8默认):多个线程共同工作
- ParNew:采用复制算法,作用于新生代
- ParallelOld:采用标记整理算法,作用于老年代
- CMS垃圾收集器(并发):采用标记清除算法,针对老年代
- 初始标记(需要暂停)
- 并发标记
- 重新标记(需要暂停)
- 并发清除
- G1垃圾收集器
- 串行垃圾收集器:只有一个线程工作,适合堆内存小的情况
G1垃圾收集器(jdk9后默认)
响应时间和吞吐量兼顾,摒弃了传统的物理分区方式
可预测的停顿时间
划分为多个区域,每个区域都可充当Eden、Survivor、Old、humongous(为大对象准备)
维护一个Collect Set集合。这个集合记录了待回收的Region块的信息
当创建新对象速度快于回收速度,将会触发FullGC
采用复制算法,分三个阶段:- 新生代回收
- 创建对象时,选择空闲区域作为Eden区进行存储。
- 当Eden内存不足时,挑选一个空闲区域作为Survivor区,进行回收。(暂停用户线程)
- 当Eden内存再次不足时,对Eden区与Survivor区进行回收。移动到新Survivor区,其中较老的对象移动到Old区。
- 并发标记
- 当老年代占用内存超过阈值(默认45%),触发并发标记(不暂停用户线程)。
- 重新标记,需要暂停用户线程。
- 混合收集
- 对Eden区与Survivor区进行回收,移动到新Survivor区
- 优先回收价值高的区域,目标Old区复制到新Old区
- 新生代回收
引用的类别
- 强引用
- 只有所有
GC root
都不通过强引用引用某对象时,该对象才会被回收。
- 只有所有
- 软引用:使用SoftReference类
- 某对象仅被软引用时,在垃圾回收后,内存仍不足时,该对象会被回收。
- 弱引用:使用WeakReference类
- 某对象仅被弱引用时,在垃圾回收时,不论内存是否充足,该对象都会被回收。
- 虚引用:必须配合ReferenceQueue使用
- 被引用的对象回收时,虚引用对象会被加入到ReferenceQueue中,由ReferenceHandler线程处理。
- 强引用
JVM调优的参数
- 设置堆空间的大小
- 初始大小:默认为物理内存的1/64
- 最大大小:默认为物理内存的1/4
- 虚拟机栈的设置
- 默认为1M
- Eden和Survivor区的比例
- 默认为8:1:1
- 晋升阈值
- 默认为15,可取范围为0-15
- 垃圾回收器
- 设置堆空间的大小
JVM调优工具
- jps
- jstack
- jmap
CPU占用高的排除
- top:查看占用CPU高的进程。
- ps:查看指定进程中的线程占用CPU的信息。
- jstack:定位问题