jvm、jre、jdk之间的关系
java virtual machine java 虚拟机,
java runtime environment java运行环境,包括jvm和基础类库
java development kits java开发工具包 ,包括java运行环境和开发工具
内存结构
程序计数器
作用:记录下一条jvm指令的执行地址
特点:
线程私有
不会存在内存溢出问题
虚拟机栈(stack)
每个线程运行时所需要的内存,叫栈
栈由多个栈帧(frame)组成,栈帧指的是每次方法调用时所占用的内存
每个线程只能有一个活动栈帧,对应着正在执行的方法
线程运行所需要的内存空间
栈帧:方法运行所需要的内存空间
栈帧中包含参数、局部变量、返回值
先进后出
jstack pid 查询进程详情
常见异常:stackOverFlowError,outOfMemory
本地方法栈
调用本地方法时,即native方法所在的区域
堆
堆是java虚拟机管理内存最大的一块内存区域,因为堆存放的对象是线程共享的,所以多线程的时候也需要同步机制。
-Xmx(最大值)和-Xms(最小值)
方法区
jdk8中改为元空间
保存jvm加载过的的类的信息、包括常量、静态变量。在类加载的时候就保存在了这个区
存在垃圾回收:用户自定义类加载器加载的一些类,可能会被垃圾回收
具体包含:类型、字段元信息、方法、静态变量、对类加载器的引用、对class的引用
垃圾回收
如何判断对象可以回收
1.1 引用计数法:无法解决循环引用的场景
1.2 可达性分析法:GC roots对象
1.3 四种引用
- 强引用
- 只有所有GC roots对象都不通过【强引用】引用该对象,该对象才能被垃圾回收
- 软引用(softReference)
- 仅有【软引用】引用该对象时,在垃圾回收后,内存仍不足时会再次触发垃圾回收,回收软引用对象
- 可以配合引用队列来释放引用自身
- 弱引用
- 仅有【弱引用】引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
- 可以配合引用队列来释放弱引用自身
- 虚引用
- 必须配合引用队列使用,主要配合ByteBuffer使用,被引用对象回收时,会将虚引用入队,由Reference Handler线程调用虚引用相关方法释放直接内存
终结器引用
标记清除算法:先标记,再清除(记录回收内存起始地址),会产生内存碎片
- 标记整理算法:先标记,再整理,清除,不会产生内存碎片,但是整理过程比较费劲,速度慢,涉及到引用地址的修改
- 复制算法:先标记,后复制(该步骤就会清理掉内存碎步),再交换,效率高,但是会占用双倍的内存
分代垃圾回收
Minor GC:只对新生代进行垃圾回收
Full GC:全部区域进行垃圾回收
- 新生代
- 伊甸园
- 幸存区from
- 幸存区to
- 老年代
步骤:
- 对象首先会分配在伊甸园区域
- 新生代空间不足时,触发minor GC,伊甸园和from存活的对象使用copy复制到to中,存活的对象年龄加一,并且交换from to
- minor GC会引发stop the world,暂停其他用户的线程,等垃圾回收结束,用户线程才恢复运行
- 当对象寿命超过阈值时,会晋升至老年代,最大阈值时15次,存储在对象头中(4bit)
当老年代空间不足,先进性minor GC,如果空间仍不足,那么触发full GC,也会stop the world,时间更长
垃圾回收器
串行(SerialGC)
- 单线程
- 堆内存较小,适合个人电脑
- 吐量优先(ParallelGC)
- 多线程
- 堆内存较大,多核cpu,适合服务器
- 让【单位时间】内stop the world的时间最短
- 响应时间优先(CMS)
- 多线程
- 堆内存较大,多核cpu,适合服务器
- 尽可能让【单次】stop the world的时间最短
- G1
类加载核字节码技术
类加载器
名称 加载哪的类
Bootstarao ClassLoader (启动类加载器) JAVA_HOME/jre/lib
Extension ClasssLoader (拓展类加载器) JAVA_HOME/jre/lib/ext
Application ClassLoader (应用程序类加载器) classpath
自定义类加载器
双亲委派模式
线程上下文类加载器
运行期优化
即时编译:在代码运行期间,通过识别热点代码,进行优化,将字节码翻译成机器码,
逃逸分析:判断变量是否存在逃逸,如果没有,就不创建该对象
方法内联:
内存模型JMM
定义了一套在多线程读写共享数据时(成员变量、数组),对数据的可见性、有序性、和原子性的规则和保障
synchronized:
既可以保证可见性,也可以保证原子性
volatile 易变性(避免从高速缓存中读取数据,强制从主存中读取;可以禁用指令重排):
不能保证操作的原子性,比如i++;只适用于一个写,多个读
有序性的理解:
在单个线程内,在保证不影响执行结果的前提下,jvm可能调整命令的顺序,这种特性称之为【指令重排】
乐观锁和悲观锁
CAS 乐观锁,先使用,再检验,可以实现无锁并发
synchronized悲观锁,先锁住,再使用
原子操作类
java.util.concurrent
synchronized内部优化
线程之间无竞争,开启轻量级锁,如果有竞争,先自旋,如果还获取不了锁,就开启重量级锁
偏向锁:
reentrantlock
五大内存区域: