----------------------
1)JDK > JRE > JVM
2)Person.java--->javac编译器(源码文件到类文件)-->Person.class-->交给JVM运行
(1)javac: 编译原理的知识,把源码文件进行:词法、语法、语法树、字节码生成器-->生成对应的字节码文件Person.class文件
Person.class 是一个二进制/16进制的表示方式--》我们看不懂,但是JVM能看懂。
(2)Person.class
ClassFile{
u4 magic; -->class文件规范的识别。说明是一个class文件
}
cafe babe 0000 0034-->ca表示u1
(3)Kotlin的运行原理:只要有自己的编译器,按照Class文件的规范,生成classFile的格式,那么就交给JVM运行了。
(4)如何交给JVM?
1.类加载机制: 从class文件到jvm:
找到类文件的位置--》磁盘,全路径。
类装载器ClassLoader
不同的类装载器去装在不同目录下的类:
双亲委派机制:有一个装载类的请求的时候,先不自己装载,先找父亲装载。。如果都装载不到。那么再向下装载。
load(){
findParent(){
}
}
loadClass方法重写一下就。
打散。
JVM:
Runtime Data Areas: 状态数据到底怎么放。
Heap:
破坏的方式:
1.
2.
交的顺序:类文件的信息交给JVM。
类文件所对应的对象Class交给JVM
2.连接:
a-验证
保证被加载的类。
b-准备
为类的静态变量分配内存空间,将其的值初始化为默认值。
c-解析
将类的符号引用转化为直接引用。~~直接根据真实地址。
3.初始化
============================
解析: 符号引用--》直接引用
1)JVM运行时数据区
运行时常量池
java虚拟机栈:线程执行方法的描述。
本地方法栈:Java调用C语言。
pc register: 当前方法执行到的位置。
2)JVM的本质:
一直往方法区放东西,
OOM
方法区(Method Area)是共享的。
3)
一个线程的创建代表一个栈
方法的调用:栈帧
4)字节码指令
5)不仅以16进制的方式 还可以 字节码指令的方式查看(javap命令)
-------------JVM进行篇-------------
1)局部变量表 和 操作数栈的存在。
2)栈里面的元素可以指向堆。
3)每一个Java对象要维护一个信息,自己属于哪个Person。 多少次GC:
Java对象的内存布局:
对象头:
MarkWord: 锁状态的标志
实例数据
对齐填充
4)对象的年龄: 垃圾回收1次,但是还没有被回收,岁数增加1。 老年代和新生代。
比如: 15次垃圾回收,还没有回收到。
老年代有对象: 200M
新生代: 100M
Eden: 80
让Eden区相对连续。
Surviror: 10
s0
s1
浪费10%的空间。
垃圾回收,就是回收没用的对象。
15次是默认的。
5)垃圾回收,会影响业务代码的执行。
空间是够得,但是空间不连续。导致分配失败。
6)担保机制: 向老年代借一点内存。
7)Java visual vm
(1)内存空间设置(VM options):
-Xms20M -Xmx20M
-XX:MetaspaceSize=20M -XX:MaxMetaspaceSize=20M
-Xss128K 设置java虚拟机栈的深度
(2)新的对象是分配在Eden区,Surviror的s1和s2一直切换。
然后每次切换,就借用Old区的内存空间。
(3)Eden:s0:s1 = 8:1:1,
新创建的对象都是朝生夕死的。
Eden过小则动不动就GC,
YoungGC(包含了Eden,S区): Minor GC
OldGC: Major GC--通常伴随着Minor GC,也就是意味着会触发FullGC
Young+Old: Full GC--》引起STW,进来可能要减少GC的频率。(允许一定范围的YoungGC)
S0和S1一定要有一块是空的,S0和S1是在不断的相互复制,但是总是有一个是空的。
每一次GC没对象的分代年龄+1,
8)栈:
过大:影响线程数目的创建
最小:出现大的循环,或者递归,很容易溢出。
9)字节码指令,谁帮我们运行?
10)Native方法库,谁去帮我们调用?
11)什么样的对象是垃圾?
(1)引用计数
有人用我,我就不是垃圾。
缺点:但是彼此之间,相互持有引用,那么就都不是垃圾。 这样就有问题。
(2)可达性分析
选择出一个GCRoot,由它触发,看看某个对象是否可否可达。
谁能称为GCRoot:GCRoot要一直存在
12)该如何回收(回收算法)?
13)垃圾收集器: 各个的优势略势,如何选型?
14)比较关注停顿时间--》降低了堆吞吐量的要求。
15)G1:
(1)尝试满足最小的停顿时间。 并尽量最高的吞吐量。
(2)初始标记--》并发标记--》最终标记--》筛选回收(根据region的使用情况,使用率较小,就认为能很快回收掉了,就回收)。
(3)Region: 区域,对堆进行了重新布局,理论上存在Young、Old、Eden,物理上已经不是隔离的了。
16)CMS: 傻傻的等着
17)G1的目标: 为了满足停顿时间
18)JVM调优的2个维度:
(1)GC日志:
停顿时间[set pause time]:垃圾收集器进行垃圾回收Client执行相应的时间--》很好的体验--》和用户交互比较多的场景、web程序。
吞吐量:运行用户代码时间/(用户代码时间+垃圾收集时间):用户代码执行占用CPU资源的时间比较大,跑任务,运算的任务。
(2)内存使用的维度:
19)停顿时间小:CMS 20ms、G1(设置停顿时间15ms,但是不能太严格) --》 并发类的收集器
20)Parallel Scanvent + Parallel Old: --》 并行类的收集器
吞吐量有限
21)Serial 和Serial Old--》内存比较小,嵌入式设备。
22)JDK: Server--Client JDK往上,WEB开发,低停顿。
23)WEB的极致: 干脆没有停顿时间。
停顿时间是要不断追求的:
24)ZGC:
停止暂停用户线程的时间 < 10ms
25)调整堆的大小
---------------------------JVM调优实战--------------
-----------JVM实战篇-----------
1)通过参数告诉JVM:
(1)标准参数(不随着java版本变化)
java -version/-help
1.8.0_31
(2)-X(用的不多)
会随着jdk版本而变化
混执行、解释执行(-Xint)、编译执行
(3)-XX(用的多)
a-Boolean类型
-XX:[+/-]name 启用或者停止
b-非Boolean类型
-XX:name=value
-XX:MaxHeapSize=100M
(4)其它
-Xms100M ==>-XX:InitialHeapSize=100M
-Xmx100M
2)修改参数
(1)VM options: -XX:+PrintFlagsFinal
(2)java -XX:+useG1GC xxx.jar
(3)tomcat---bin---xxx.sh/catalina.sh --> jvm参数
(4)实时修改不重启--》jinfo修改
3)
1Byte 8bit
1kb 1024Byte
1MB = 1024kb
1GB 1024MB
104857600byte --> 100MB
4)JVM调优参数利器1: JVM参数
5)JVM调优参数利器2: 命令(当前JVM现在情况)
线程信息
jps --> 查看java当前进程,得到pid
jinfo -flag UseG1GC pid -->某一个pid java进程
实时修改某个进程中的JVM参数的值: jinfo -flag name=value PID(条件)
jstat -class 60356 1000 10
jstat -gc 60356 1000 10
jstack pid: 查看当前进中所有线程堆栈信息
java代码中有了死锁,就可以查看出来。
jmap: 生成堆内存的快照:
2Core 4G: 生产环境有时候会发生OOM,在发生OOM时,能够把堆内存的信息拿到
jmap -dump:format=b.file=heap.hprof PID
在发生OOM时,产生dump:
-Xms20M -Xmx20M -XX:+HeapDumpOnoutOfMemoryError -XX:HeapDumpPath=heap.hprof
6)工具 bin/
(1)jconsole
(2)jvisualvm
本地的
远端的:
jmx
(3)arthas
(4)EMA
(5)perfma
7)排查什么?
(1)堆使用: OOM
根据heap文件
GCRoot可以是Thread
(2)gc.log
8)调优观察: 吞吐量和停顿时间在调优过程中的一些变化。
===========================
1)JVM核心架构
(1)Class File-->Class Loader-->运行时数据区
(2)垃圾收集器
2)垃圾收集发生的时机是什么时候?
Minor GC:
Eden 区或者S区不够用了
Young的GC
Major GC:
老年代空间不够用了
Full GC(Minor GC + Major GC[通常还伴随着Minor GC] + MetaSpace GC):
方法区空间不够用了,也会触发GC
System.gc():
只是发送一次垃圾回收的通知,但是实际发生GC时,还是由于JVM去决定。
但是,无缘无故的插入垃圾收集请求,这是不好的。而且会暂停用户线程的执行。
3)UseParallelGC(默认的)
(1)新生代和老年代的GC: 关注吞吐量。
(2)大多数对象都是朝生夕死的。
(3)Old区还会释放出来。
4)CMS(复杂了很多)
(1)适用于老年代的。。因此新生代的不知道用啥GC,所以就搞了个ParNewGC的用于回收新生代的。
5)GC(Young区 + 部分Old区,逻辑上是连续的。所以叫做mixed,而不是Full GC)
(1)堆内存使用率45%以上。
(2)
6)GC easy
7)JVM调优
内存空间不够。
gc次数太多--》用户线程代码执行受影响-->cpu使用会高。
堆大小变大:
利:GC次数减小
弊:停顿时间增加了一点点(对象很大,那打扫就慢了)。
停顿量: 15ms..过小时,就回收不了那么多对象。过多垃圾对象,因此gc次数增加。
8)G1和CMS的区别?
(1)CMS: 标记--》清除
Card Table: 记录一下具体引用了谁。这样不用全盘扫描整个老年代。
(2)G1: 标记---》整理
划分为一个个的region
每一个region大小相等,但是还保留了:新生代、老年代、大对象。 但是逻辑空间上连续。
减少内存空间的碎片问题。
收集的方式Gabarage优先: 先收集垃圾比较多的区域。
Remembered Set: RSet,是谁引用了我。 新生代老年代不必连续。
G1至少6G以上内存。2C4G的则不太适用。
PC机代替IBM小型机。
9)高并发场景之下,如何减少Full GC的次数?
可以增加Young区大小
10)GC次数太高
(1)打印出gc日志
(2)增加堆内存空间
(3)垃圾收集器不合适。 选择G1
(4)停顿时间太严格了。
11)CPU飙升
(1反问:问什么CPU会飙升呢?
(2)top id jstack、jinfo、jamp
(3)集群
(4)mq(实现延缓处理)
(5)线程死锁的问题
12)内存泄漏 和 OOM
(1)泄漏: 已经是垃圾,但是无法被回收. 最终导致OOM
(2)
================================================
----------------Tomcat基础篇---------------
1)Servlet容器: Tomcat或者Jetty
2)Tomcat的2个称呼
(1)Web容器: 能够接收客户端的http请求
(2)Servlet容器:因为有一套Servlet规范。请求过来之后,我如何处理呢?于是就有处理规范的定义。
Class LoginServlet extends HttpServlet{
doGet
doPost
}
还要配置一下web.xml中的Servlet配置。
-------------
自己开发的话:
自己accept。
然后InputStream、OutputStream处理请求。
------------
既然有人在JavaEES中定义了一套Servlet规范:
项目中有多少Servlet,就放到一个List中维护。
Tomcat就是对Servlet规范的一种实现。
(3)如果是SpringBoot内置的Tomcat,也会有相应的Tomcat中初始化的代码吗?
(4)Context标签:代表了一个Web项目。
(5)Connector-->Engine(管理多个虚拟主机,相当于负载均衡器)--》Host
虚拟主机/web应用/路由
www.gupao.com/jack/LoginServlet
3)为什么设计的如此复杂性?
高内聚,低耦合。
4)Tomcat核心架构
(1)对外:Connector(由于可以在不同的端口监听;应对ARP、https等不同方式请求,所以可以有多个Connector)
监听完端口,必须进一步细分:
ProtocolHandler:
EndPoint--》对Socket的封装。
Processor--》通过应用层的方式,转化为Http协议层的Request和Response
Adapter: 适配器,转化为Servlet的Request和Response
(2)对内:Engine-->Host-->Context
Container
Engine
Host
Wrapper(Servlet)
5)设计模式
工厂、包装、组合 设计模式。
高内聚,低耦合。
Adapter:适配器模式。
LifecycleBase:
实现了Lifecycle,是模板方法的实现。
6)默认IO方式什么?
(1)在配置文件中指定
在Tomcat 8.版本, 默认是NIO
7)打破双亲委派机制
(1)重写
findClass
loadClass
(2)
8)Engine里面是Host
---------------------
1)优化的维度:
(1)修改源码编译是一个维度~困难
(2)修改xxx.xml
2)性能指标的检测
(1)吞吐量、响应时间、错误数、线程池、CPU、内存[Tomcat java进程]等
(2)ps -ef | grep tomcat pid
cat /pro/pid/status
top -p pid
(3)毕竟tomcat跑起来就是一个java进程
jconsole
jvisualvm
arths
(4)tomcat自带的监控界面
(5)psi-probe等
3)优化的点
(1)Connector:
IO:
BIO(并发低)
NIO(并发高)
APR
jmeter进行压测:观察吞吐量和响应时间 在不同并发情况下,使用不同的IO,最佳值。
减少不必要的Connector
GC过多,可能导致stw
(2)
Host
Context
Executor(max:200 min:25) --》线程池数量的设置
BIO下,超过200个,如果不归还,就进入等待队列。 是否可以释放调大。
线程过多导致占据内存多。 CPU切换过多。 最终切换不过来,就崩了。
(3)删掉没用的Listener,监听。
4)Tomcat还可以处理静态资源
动静分离:减少Tomcat的压力。
本地缓存
nginx
CDN(就近)
5)Tomcat
Session用一个统一的管理机制。
6)
(1)去掉无用的web项目,避免tomcat加载无用的项目,加速启动。
(2)让更多线程参与部署项目,加速启动。
(3)Connector,数据压缩 > 100byte时,才进行压缩。
7)Tomcat部署完web时,cpu使用率比较高。 为什么?
(1)为什么会高呢?
我用top命令
(2)一定是线程数多导致的。 线程是由于cpu调度的。
1:业务代码逻辑,创建过多线程。 线程进行频繁上下文切换。
2:java层面,gc太频繁了。 gc会引起垃圾回收线程,抢夺cpu资源。
8)排查tomcat部署web后,cpu过高问题的思路:
ps -ef | grep tomcat -->pid
top -H -p pid 查看某个进程中,线程使用cpu的情况。
已经知道了tomcat进程中哪个线程cpu占用率高 thread-jack
jstack pid -->定位到thread-jack 是不是发生了死锁。 递归。 死循环等。
并发编程中:线程的各种状态。
9)tomcat经常拒绝连接
(1)BindException--》Address already JVM_Bind,说明端口被占用了。
netstat -an tomcat换个端口
(2)ConnectException:refused
检查一下服务器的:ping IP
(3)SocketException:
Too many open files -->并发太高,文件句柄不够用了。 关闭一些无用的文件句柄。
增加文件句柄。 ulimit -n 10000
10)Tomcat内存溢出
JVM内存溢出
11)类加载器
BootStrap ClassLoader
Ext ClassLoader
App ClassLoader
Custom ClassLoader:
如果用双亲委派模型时,如果在不同的web应用下,同名的同全路径下的,
Web1
LoginServlet
Web2
LoginServlet
需求:
2个Web应用隔离。
2个Web应用要共享某一个工具类。如:Spring类,共享,只需要加载一次。
而jvm双亲委派机制,只能有一份。
因此不能简单用ClassPath下面的、
所以:
WebApp ClassLoader,给每一个应用都创建一个WebApp ClassLoader,利用里面的loadClass和findClass复写下,
最终:首先加载自己的,再加载自己的。就实现了:既能属于自己,又能实现共享。
Shared ClassLoader: 保证双亲委派模型(共享类)。 Catalina ClassLoader(Tomcat和web应用之间的隔离)
Common ClassLoader:
Tomcat类和Web类做隔离。
12)双亲委派模型的本质: 解决JVM中如果存在2个同样的全路径类怎么办?
13)Tomcat为什么要打破双亲委派机制(不是完全打破): 为了实现Tomcat遇到的各种各样的需求。
14)线程上下文类加载器