JVM入门到入魔

在这里插入图片描述

jvm

跨语言的平台;只与class文件有关
java从编码到执行

在这里插入图片描述
常见jvm 实现:

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.调用类初始化代码<clinit>  ,给静态成员变量赋初始值

小总结:
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加载路径

在这里插入图片描述

自定义加载器—重写findClass方法—同时是模版模式
在这里插入图片描述

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 导出堆文件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值