咕泡学院~JVM

----------------------
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)线程上下文类加载器

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值