JVM即Java虚拟机,主要用来编译,执行Java代码,并在执行过程中对内存进行管理.
Java代码的执行流程
Java代码首先要通过javac前期编译器从.java文件编译为.class字节码文件(注解处理),然后将.class文件交由虚拟机的类加载器ClassLoader进行装载(采用双亲委派机制进行装载),将.class文件装入java虚拟机后,Hotspot虚拟机根据配置的模式(混合模式,解释模式,编译模式)来决定使用解释器直接解释执行还是使用JIT即时编译器进行编译执行,亦或者是两者混用的混合模式进行执行.默认采用混合模式执行.
- Hotspot虚拟机内置了两个即时编译器,称为client编译器和server编译器(C1,C2编译器),前者进行一些稳定可靠的优化,后者还会进行一些激进不可靠的优化策略.后者编译出的机器码相对前者质量要高,而前者进行编译的速度要比后者快.
- 可靠的编译优化:热点代码替换,栈上替换.原理:基于采样的热点探测,方法调用计数器和回边计数器. 方法内联
- 基于逃逸分析的编译优化:栈上分配,标量替换,同步消除.
java运行时数据区
程序运行后要对内存进行管理,Java虚拟机的运行时数据区分为堆,虚拟机栈,本地方法栈,程序计数器,元空间
堆中存放着对象(大小不固定),虚拟机栈中存放着对象的引用(4byte)和基本数据类型(大小固定)(堆栈分离的好处:对象可以多栈共享,采用面向对象的方式进行统一处理).
本地方法栈用来执行本地方法,虚拟机栈用来执行java方法.
程序计数器类似于字节码的行号指示器,用于取字节码指令.
元空间用于存放类的元数据信息(包括静态变量和字符串常量池(字符串的引用),方法代码,方法数据),元空间的大小受限于本机的虚拟内存大小.而虚拟内存大小受本地物理内存和地址指令位数的限制.
直接内存不属于Java运行时数据区的一部分,可以用NIO的通道和Buffer来直接操作这块内存,避免了java堆和native堆的来回复制数据,提高了性能.
栈中对象的引用和基本数据类型随着栈帧的出栈而销毁.而堆中的对象则由Java虚拟机的垃圾收集器进行检测回收.
如何确定哪些对象需要回收?
- 引用计数法(简单,循环引用会导致无法回收,有内存泄漏风险).
- 可达性分析(HotSpot采用,枚举根节点GC Roots(虚拟机栈中引用的对象,本地方法栈中引用的对象,static属性引用的对象,final属性引用的对象),会导致Stop The World)
什么时候回收?
到达安全点或者安全区域的时候.
如何到达安全点,主动式中断.
垃圾回收算法
常用的垃圾回收算法分为三大类,标记清除(简单,产生碎片,提取触发),复制(空间浪费),标记整理(无碎片,移动存活对象费时)
堆分为新生代和老年代,新生代分为Eden区和两个Survivor区,大小比例为8:1:1(防止空间浪费),新生代采用复制算法(使用老年代进行分配担保,GC分代年龄阈值为15,默认为1).老年代采用标记整理算法(回收质量低,速度慢,存活周期长).
动态对象年龄判定,幸存者区某年龄对象大小之和存活超过幸存者区一半大小,大于等于该年龄的对象直接晋升老年代.
空间分配担保,Minor GC前判断,老年代可用连续空间>新生代对象总大小|历次晋升对象大小的平均值,则进行Minor GC,否则进行Full GC.
Minor GC(新生代)、Major GC(老年代)和Full GC(新生代+老年代)
Java中的四种引用
强引用(不回收),软引用Soft Reference(下一次溢出前回收),弱引用Weak Reference(下一次直接回收),虚引用Phantom Reference(回收时通知)
垃圾收集器
serial(新生代)单线程,复制算法,client模式下默认.
parnew(新生代)多线程,收集线程数与cpu核心数量一致.
parallel scavenge(新生代)多线程,复制算法,关注吞吐量.GC自适应调节策略.
serial old(老年代)单线程,标记整理算法,client模式下与serial联用.
parallel old(老年代)多线程,标记整理算法,关注吞吐量.
CMS(老年代)多线程,标记清除,并发低停顿,CPU资源敏感(少于4个性能下降),会产生空间碎片,无法处理浮动垃圾,用serial old做后备预案.
G1(整个堆)整体标记整理,局部复制,划分区域,判断价值,追求低停顿.
垃圾收集器的搭配使用
serial+serial old
parallel scavenge+parallel old
Serial+CMS+Serial Old
ParNew+CMS+Serial Old
parnew+serial old
Parallel Scavenge+Serial old
G1
类的生命周期
加载>>连接(验证,准备,解析(词法分析,语法分析))>>初始化>>使用>>卸载.
准备阶段为类变量赋零值.final直接赋程序中的值.
初始化阶段执行类构造器给类变量赋值,还会初始化其他资源.
双亲委派模型
启动类加载器Bootstrap ClassLoader 加载/lib目录下的 获取方法:ClassLoader.getSystemClassLoader().getParent().getParent()
扩展类加载器Extension ClassLoader 加载/lib/ext目录下的 获取方法:ClassLoader.getSystemClassLoader().getParent()
系统类加载器Application ClassLoader 加载Classpath目录下的 获取方法:ClassLoader.getSystemClassLoader()
自定义类加载器Custom ClassLoader 定义方法:继承ClassLoader抽象类,重写findClass()方法.
双亲委派模型用于保持java程序的稳定运行.
通过线程上下文类加载器Thread Context ClassLoader可以破坏双亲委派模型.
破坏双亲委派模型的典型应用场景,代码热替换,模块热部署.原理:连同类加载器一同卸载.
重载与重写
静态单分派和动态多分派,静态是编译阶段确定,动态是运行阶段确定.方法的返回类型参与重写不参与重载.宗量,方法的接收者与方法的参数.
java内存模型
java使用的是轻量级进程与内核线程一比一的模型.优点,不会阻塞整个进程,缺点,用户态与核心态切换,轻量级进程数量受限于内核资源.
内存分配的两种方式:
指针碰撞,内存规整,一个指针分界.适用于Serial收集器和ParNew收集器.
空闲列表,内存不规整,维护列表记录可用内存块,适用于CMS收集器.
线程安全
volatile语义:保持可见性,禁止JVM的指令重排序.
CAS比较并交换原理,原子操作,内存位置V,旧值A,新值B,CAS存在的ABA问题.
锁优化
自旋锁,适合锁定短时间.
自适应自旋锁.
锁消除,同步消除,依赖于逃逸分析(方法逃逸,线程逃逸).
锁粗化,扩大锁粒度.
虚拟机参数:
-version查看版本及运行模式
-Xmixed使用混合模式,启动时使用解释器解释执行,程序运行后使用编译器编译热点代码为本地机器码,如果运行在server模式下,还会进行激进优化,如果激进优化失败,则退回到解释执行.
-Xint使用解释模式,全部解释执行.
-Xcomp使用编译模式,优先编译执行,无法编译时使用解释器解释执行.
-client使用client模式,不会进行激进优化.
-server使用server模式,会进行激进优化.
-XX+printGCDetails打印GC日志详细信息
-Xms堆的初始大小
-Xmx堆的最大大小
-Xss线程空间大小
-Xmn新生代大小,通常为-Xmx的三分之一或者四分之一
-XX:UseStringCache默认开启,缓存常用的字符串
-XX:PrintGCDateStamps打印GC的耗时
-XX:MetaspaceSize初始元空间大小和元空间GC阈值
-XX:MaxTenuringThreshold分代年龄的阈值
-XX:PretenureSizeThreshold大对象直接进入老年代阈值
-XX:NewRatio新生代与老年代的比例
-XX:SurvivorRatio新生代Eden和Survior的比值,默认为8.
-XX:PermSize永久代初始大小,1.8后自动忽略
-XX:MaxPermSize永久代最大大小,1.8后自动忽略.
-XX:+HeapDumpOnOutOfMemoryError让虚拟机在发生内存溢出时 Dump 出当前的内存堆转储快照,以便分析用
-XXHeapDumpPath=/home/admin/logs指定堆转储文件的路径.
-XX:+UseSerialGC使用指定收集器
-XX:+UseParallelGC
-XX:+UseParallelOldGC
-XX:+UseConcMarkSweepGC
-XX:+UseG1GC
来源:CSDN
作者:小锴的学习笔记
链接:https://blog.csdn.net/qq_40925525/article/details/104429775