jvm
跨语言的平台;只与class文件有关
java从编码到执行
Hotspot j9-IBM Microsoft VM TaobaoVM
JDK :java 开发工具包,包含jre 和jvm
jre:java 运行时环境,含核心类库
jvm :仅用于执行
class 文件时一个二进制的字节流
?? volatitle 修饰long double 64位 是原子性的–》jvm 底层implentments 就会当作原子性处理。
java 8大原子操作:
lock(锁定):作用 与内存,把一个变量标记为一个线程独占状态
read(读取):作用 于内存,它把变量值从主内存传送到线程的工作内存中,以便随后的load动作使用
load(载入):作用于工作内存,它把read操作的值放入工作内存中的变量副本中
use(使用):作用于工作内存,它把工作内存中国的值传递给执行引擎,每当虚拟机遇到一个需要使用这个变量的指令时,将会执行这个动作
assign(赋值):作用于工作内存,它把从执行引擎获取的值赋值给工作内存中的变量,每当虚拟机遇到一个给变量赋值的指令时,执行该操作
store(存储):作用于工作内存,它把工作内存中的一个变量传送给主内存中,以备之后的write操作使用
write(写入):作用于主内存,它把store传送值放到主内存中的变量中
unlock(解锁):作用于主内存,它将一个处于锁定状态的变量释放出来,释放后的变量才能被其他线程锁定。
class 加载过程
class load 到内存生成2部分内容:
1。二进制 放到内存一块区域
2。生成class类文件,class类对象指向1
加载过程
1.Loading
1.双亲委派
2.LazyLoading 五种情况
-new getStatic putstatic invokestatic 指令,访问final变量除外
-java,lang.reflect对类进行反射调用时
-初始化子类的时候,父类首先初始化
-虚拟机启动时,被执行的主类必须初始化
-动态语言支持java.lang.invoke.MethodHandle解析的结果REF_getstatic REF_putstatic REF_invokestatic的方法句柄时,该类必须初始化
3.ClassLoader的源码
1.findInCache-->parent.loadClass-->findClass()
4.自定义类加载器
1.extends ClassLoader
2.overwrite findClass()-->defineClass(byte[] ->Class clazz)
3.加密 最简单s^seed加密, seed^seed 解密 seed=自定义常量
5.混合模式:编译执行 解释执行
1.检测热点代码:-XX:CompileThresshold=10000
2.Linking
1.Verification
1.验证文件是否符合JVM规定
2.Preparation
1.静态成员变量赋默认值
3.Resolution
1.将类、方法、属性等符号引用解析为直接引用
常量池中的各种符合引用解析为指针、偏移量等内存地址的直接引用
3.Initializing
1.调用类初始化代码 ,给静态成员变量赋初始值
小总结:
load-默认值-初始值
new-申请内存-默认值-初始值
java内存模型:
JMM
硬件层数据一致性
协议有很多,intel 用的MESI
现代cpu的数据一致性实现=缓存锁(MESI…)+总线锁
读取缓存以cache line(缓存行) 为基本单位,目前为64字节
伪共享----位于同一缓存行的两个不同数据被两个不同 CPU锁定,产生互相影响。
—伪共享问题解决:缓存行对齐–空间浪费了些,但相对会提高效率–在开源disruptor 就用了这个。
乱序问题
CPU 为了提高指令执行效率,会在一条指令执行过程中(比如去内存读数据<慢100倍>),去同时执行另一条指令,前提是,两条指令没有依赖关系。
WCBuffer (写操作也可进行合并)
乱序执行证明:JVM/jmm/Disorder.java git上
如何保证特定情况下不乱序
volatile 有序
类加载器
加载不同的class
双亲委派原则—为了安全。
某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,
如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,
才自己去加载。
类加载器的范围—来自Launcher源码
-sun.boot.class.path
Bootstrap ClassLoader加载路径
-java.ext.dirs
ExtensionClassLoader加载路径
-java.class.path
AppClassLoader加载路径
gc:
Why
what 引用计数 为0 对象无引用;但不可解决循环引用问题
可达性分析:gc root 识别首个有引用的对象,按关联顺序查找。
GC ROOTs 根节点:线程栈的本地变量、静态变量、本地方法栈变量等。
How gc 算法:
mark-sweep 标记清除 --产生内存碎片
mark-copying 标记复制– 只用一半,将标记有用对象拷贝到另一半-----浪费空间
mark-compact(标记-压缩): 整理将未使用放前,存活放后–速度慢(一边清理 一边整理)
分代收集算法
hotspot 有10中垃圾回收器:。。。。
6个分代:
3个非分代:个、G1(jdk1.9) 逻辑分,zgc
一般默认(jdk1.8)ps+po,
堆内存逻辑分区(经验说):1/3(8:1:1)新生代:2/3 老年代
新对象 eden 进过一次YGC未回收 就年龄+1
新生代大量死去,少量存活,采用复制算法,时间较短
老年代存活率高,回收较少,采用mc或ms ,什么时候回收呢:full gc(新生 老年都回收) 或者
当OOM 时,老年对象都存在引用, fgc 无用 ,就会stw
全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,
但不能与JVM交互;这些现象多半是由于gc引起。
GC 有两种类型:MinorGC (ygc)和Full GC
JVM收集器
垃圾收集器使用发展及情况
serial 单线程回收时工作停止,适用内存小的几兆到几十兆 --》
parallel (多线程回收时工作暂停),还是会有卡顿—》
cms (并发,垃圾回收和工作线程同时,解决卡顿,并发标记,会有个问题:被标记,但是又被引用。会漏标或错标->三色标记算法)
GC 会使工作线程全部暂停
gc算法影响暂停时间长?
调优之一:重启
频繁fgc—
OOM—
cpu暴增----工具看哪些线程占用cpu 高
JVM调优
系统级的调优----》减少GC的频率和Full GC的次数。
1、FGC 针对整个堆整理,所以慢
2、FGC原因
(1) 老年代写满
---->尽量让对象在新生代多存活/被回收,
避免创建过大对象直接在老年代创建对象。
---->增加堆大小,或者调整并发标记线程数 -XX:ConcGCThreads。
(2)永久代 空间不足
—>增大永久代空间,避免太多静态对象,控制老年代 新生代比例。
(3)System.gc()被显示调用
—>垃圾回收不要手动触发,尽量依靠JVM自身的机制
(4)大对象分配失败
----> 大对象找不到合适的Region空间分配,就FGC
---->可以增大内存或者增大 -XX:G1HeapRegionSize。
(5)晋升失败
—>在 GC 的时候没有足够的内存供存活/晋升对象使用 ,就FGC
---->可以通过 -XX:G1ReservePercent 来增加预留内存百分比,
减少 -XX:InitiatingHeapOccupancyPercent 来提前启动标记,
或者 -XX:ConcGCThreads 来增加标记线程数。
性能监控工具
1、jps:用来输出JVM中运行的进程状态信息 jps [options] [hostid]
2、jstack:堆栈跟踪工具,一般用于查看某个进程包含线程的情况。* jstack [option] 进程id
3、jstat:监视虚拟机各种运行状态信息(各种堆和非堆的大小及其内存使用量),
可以显示本地或者是远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据**
4、jinfo:实时地查看和调整虚拟机各项参数 jinfo [option] pid
5、top 查看当前所有进程的使用情况,CPU占有率,内存使用情况,服务器负载状态等参数.
6、jmap 打印出某个java进程(使用pid)内存内的所有对象的情况。一般用于查看内存占用情况。
线上故障排除指南
线上故障主要会包括 CPU、磁盘、内存以及网络问题,
而大多数故障可能会包含不止一个层面的问题,所以进行排查时候尽量四个方面依次排查一遍。
1、CPU
----原因包括业务逻辑问题(死循环)、频繁 GC 以及上下文切换过多。
(1)jstack 分析问题
top 查看那个进程占用高
top -H -p pid 查看CPU使用率高的线程
printf ‘%x\n’ pid 得到nid
jstack pid | grep ‘nid’ -C5 -color 找相应的堆栈信息
通常会比较关注 WAITING 和 TIMED_WAITING 的部分,BLOCKED 就不用说了。
cat jstack.log | grep “java.lang.Thread.State” | sort -nr | uniq -c 对文件分析,waiting 特多就有问题
(2)频繁gc
jstat -gc pid 1000 对 GC 分代变化情况进行观察
gc 频繁就再针对gc进一步分析
(3)上下文切换
vmstat pid 查看频繁上下文情况
cs 列代表了上下文切换的次数
pidstat -w pid 对特定pid 监控
2、磁盘
----更多时候,磁盘问题还是性能上的问题
df -hl 查看文件系统状态。
iostatiostat -d -k -x 进行分析 --定位那块磁盘出问题
iotop 定位文件读写来源 —tid
readlink tid 找pid
cat /proc/pid/io 看该进程具体读写情况
lsof -p pid 确定具体文件读写情况
3、内存
-----主要包括OOM、GC 问题和堆外内存
free 查看内存情况
(1)堆内内存
OOM -----java栈无足够内存空间----基本上是线程池代码问题,如忘记shutdown-----jstack / jmap
若正常,可指定Xss来减少单个thread stack 的大小
—内存占用达到最大设置值----内存泄漏等
—元数据去内存达最大 同上
Stack Overflow
栈内存溢出,线程栈需要的内存大于 Xss 值,该值调整太大可能会引起OOM
使用 JMAP 定位代码内存泄漏
场景:每次请求都 new 对象,导致大量重复创建对象;
进行文件流操作但未正确关闭;
手动不当触发 GC;
ByteBuffer 缓存分配不合理等都会造成代码 OOM。
GC 问题和线程
线程的话太多而且不被及时 GC 也会引发 OOM
pstreee -p pid |wc -l 看下总体线程
(2)堆外内存
堆外内存溢出表现就是物理常驻内存增长快。
往往是和 NIO 的使用相关。
pmap 来查看下进程占用的内存情况。
4、GC问题
通过 GC 日志来排查问题
5、网络
超时(读写、连接、其他)
TCP队列溢出
jps 查看哪些线程在运行,
jmap 线程占用视图
jinfo 查看线程信息
jvm图形界面工具:
有过线上系统jvm调优经验–
命令:jstack
jmap -histo 1693 | head -20
内存过大10g ,jmap 停1个小时以上,
阿里工具:arthas
heapdump 导出堆文件