java虚拟机的内存模型
线程共享:
方法区
存储运行时常量池、静态变量、类信息、JIT编译后的代码等数据
补充说明:
方法区只是JVM规范中定义的一个概念,用于存储类信息、常量池、静态变量、JIT编译后的代码等数据,具体放在哪里,不同的实现可以放在不同的地方
实现方式1:
.Hotspot虚拟机jdk1.7版本以及之前版本的永久代就是其实现方式。存放在虚拟机中
实现方式2:
Hotspot虚拟机1.8后取消永久代,私用metaspace元空间来代替永久代,存放在本地内存
jdk升级的原因是
a 为HotSpot与JRockit融合做准备,JRockit没有永久代的概念
b 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,解决了永久代OOM问题
堆
给创建的对象分配空间【最大的一块儿】
线程私有:
本地方法栈
native修饰的方法
Java Native Interface JNI 【实际理解为natiave 本地方法】
虚拟机栈
方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
程序计数器
当前线程所执行的字节码的行号指示器
==================================================================
类的生命周期
加载 class字节码文件加载到方法区,实际是创建class对象存放到方法区的具体实现里,比如hotspot的永久代或者元数据
连接
1验证
2准备 内存分配 类中static修饰的变量分配内存并设置默认初始值,代码中定义的值在初始化的时候赋值
3解析 引用替换
初始化 静态块执行 静态变量真正赋值
参考资料
https://www.cnblogs.com/ityouknow/p/5603287.html
类加载器
Bootstrap lib下的class
ExtStrap ext包下的class
AppStrap 应用class
自定义 自定义加载器
类加载机制:双亲委派机制
==================================================================
GC算法浅析:
复制算法
缺点:浪费一半的空间,一般使用在新生代里
标记清除算法
缺点:产生内存碎片
标记整理算法
CMS 收集器
【1】初始标记(标记GC Roots能直接关联到的对象)(stop the world)
【2】并发标记(进行GC Roots Tracing的过程 间接关联)
【3】重新标记(修正并发标记期间因用户程序继续运行而导致标记产生变动的对象的标记记录)(stop the world)
重新标记的时候不会进行全扫描,而是对变化的那部分进行扫描处理,第二部分发生变动的对象所在的区域会加一个dirty标记,
因此避免了全部的扫描 提升了性能
【4】并发清除
GC Roots概念解析:
GC管理的主要区域是Java堆,一般情况下只针对堆进行垃圾回收。方法区、虚拟机栈和本地方法栈不被GC所管理,
因而选择这些区域内的对象作为GC roots,被GC roots引用的对象不被GC回收。
查看了很多的资料发现这段解释是比较清晰容易理解的
STW stop the world概念解析:
简称STW
在执行垃圾收集算法时,Java应用程序的其他所有除了垃圾收集帮助器线程之外
的线程都被挂起
性能评判:
因为CMS收集器采用的标记清除算法,会产生大量的内存碎片,导致大的数据在内存中没有连续的空间进行存储
利用率较低
G1 收集器
逻辑分代不是物理分代
G1采用了分区(Region)的思路,将整个堆空间分成若干个大小相等的内存区域
每个分区里又继续划分成N个Card 512kb
分区的好处 类似于分段锁的好处,减少了同步时间
分代的概念变成了逻辑的,指定新生代内存范围比如10%~60%,老生代内存范围,这些内存空间是动态的不是固定的
灵活多变 操作性增加
同时G1采用的是标记整理算法,而不是标记清除算法,解决了内存碎片产生的问题
https://blog.csdn.net/coderlius/article/details/79272773
G1算法详细了解文章
https://blog.csdn.net/coderlius/article/details/79272773
ZGC算法
==================================================================
原子性
可见性
可见性就是指当一个线程修改了线程共享变量的值,其它线程能够立即得知这个修改。
volatile synchronized final
有序性
volatile synchronized
指令重排序概念解析:
jvm运行的时候如果某个指令运行耗时比较长,如果等待该指令完成才去执行下一条指令的话,CPU浪费比较严重,会一直去等待
该条指令执行完成,因此可能会跳过该条指令去执行下一条指令,这就类似于多线程的目的
这种现象在jvm中是普遍的,很多人都会问,这样一定会出事的,按照这种乱序执行那可不一定会出事的,但是实际我们开发中
基本没遇到过出问题的,这是为什么呢,因为jvm为了防止出事,指定了一些规则来避免出现乱序造成的问题,也就是有些可以乱序,
有些不可以乱序 也就是happen-before原则
程序顺序原则:业务逻辑顺序原则 就是if-else for这种逻辑表达式原则
锁原则:一个unlock操作先行发生于后面对同一个锁的lock操作。这里必须强调的是同一个锁,而”后面“是指时间上的先后顺序。
volatile规则:对一个volatile变量的写操作先行发生于后面对这个变量的读取操作,这里的”后面“同样指时间上的先后顺序。
线程启动规则:Thread对象的start()方法先行发生于此线程的每一个动作。
线程终于规则:线程中的所有操作都先行发生于对此线程的终止(Thread.join())
线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测是否有中断发生。
对象终结规则:一个对象初始化完成(构造方法执行完成)先行发生于它的finalize()方法的开始。
传递性规则:如果操作A先行发生于操作B,操作B先行发生于操作C,那就可以得出操作A先行发生于操作C的结论。
指令重排序参考文档:
https://www.cnblogs.com/xll1025/p/6486170.html
==================================================================
JVM的知识点:
GC优化点:
1 方法逃逸 栈上分配
减少需要GC的对象数量,GC对象存放在堆里,也就是说减少堆里存放的对象
Java对象是在堆里分配的,在调用栈中,只保存了对象的指针。当对象不再使用后,需要依靠GC来遍历引用树并回收内存,
如果对象数量较多,将给GC带来较大压力。因此,减少临时对象在堆内存分配的数量是最有效的优化方法。
方法逃逸 栈上分配
概念解析:
逃逸场景:全局变量赋值、方法返回值、实例引用传递
方法逃逸:方法内的对象,可以被方法外所访问引用
栈上分配:没发生逃逸的对象,在栈分配空间
2 对象引用的几种方式:强 软 弱 虚
强:创建一个对象并把这个对象赋给一个引用变量。
eg:Object object =new Object();
日常开发中随处可见对象赋值
强引用有引用变量指向时永远不会被垃圾回收,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。
软:
特性
GC时,内存空间足够,GC不会回收它,内存不够才会回收
使用场景
实现内存敏感的高速缓存,比如网页缓存、图片缓存等。使用软引用能防止内存泄露,增强程序的健壮性。
弱:
GC时,不管内存够不够,GC都会回收它
虚:
跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收
参考文献
https://www.cnblogs.com/huajiezh/p/5835618.html
==================================================================JVM性能调优
jvm的性能调优主要分两块:CPU+内存
内存
分析内容:
1堆中的对象的类型,数量以及在内存中的占比
不合理的数据结构将会造成大量的无效对象,占用极大内存
2各代内存占用比例,各代GC的频率以及耗时
内存分配不当,会造成GC频繁降低系统性能
3线程状态 分析死锁等情况
常用指令:
jps 获取java进程ID
jstack dump当前线程状态
jmap -heap 进程ID 主要用于查看内存中的对象数量 内存占用
jstat gcacuse 进程Id 查看各代内存占用比例,各代GC的频率以及耗时
参考文献:
https://blog.csdn.net/Elvis_lfc/article/details/80364349
https://blog.csdn.net/u014730165/article/details/81984523
CPU
CPU热点产生的原因主要有两种,一种是频繁GC产生的,一种是低质量的代码
可以使用top指令查看当前CPU占用率最高的,性能最低效的线程ID
线程进行快照,然后定位到对应的代码
参考资料
https://blog.csdn.net/qq_29699799/article/details/80172804
JDK提供了一些开发工具,可以让我们可以直观的看到上述信息
A Jconsole
Jconsole的使用案例
https://www.cnblogs.com/panxuejun/p/8630779.html
B VirtualVM
可以查看内存 CPU 即时GC Dump线程状态 Dump内存使用详情 还可以进行CPU或者内存采样进行分析,是一款很强大的工具
VirtualVM使用案例
https://www.cnblogs.com/sxdcgaq8080/p/7156227.html