线上java项目cpu突然飙到100%怎么排查?
1、> top
查看所有进程,取第一个占用资源最多的进程pid
2、>top -Hp 进程pid
利用pid查询这个进程有哪些线程
3、printf '%x' 线程pid
查看进程的所有线程中资源占用最高的线程,将其pid转成16进制,假如值是6a4
4、jstack 进程pid > x.txt
将进程pid的内容放到t.txt文件中
5、vim x.txt
6、搜索6a4从而定位到这个线程抛出的异常,查看我们系统at的日志类,即可定位到哪个类的哪一行抛出的异常。
类加载状态过程
加载、验证、准备、解析、初始化、使用、卸载
一个类加载的双亲委派机制
类加载器分为,引导类加载器,扩展类加载器,应用程序类加载器
引导类加载器是c++实现的,主要加载java核心库,并且创建扩展类加载器和应用程序类加载器
扩展类加载器这个也太华住java扩展库中的类
应用程序类加载器主要加载我们自定义的类
类的加载流程是这样的比如com,pagker.user类,先到应用程序类加载器加载,它会判断是否被加载过,如果没有加载过就会委托扩展类加载器加载,扩展加载器同样会判断是否加载过
如果没有加载过就委托引导类加载器进行加载,引导类加载器也会判断是否加载过,如果没有加载过会从核心库上加载
有哪几种垃圾收集器?
1)七种垃圾收集器:
- Serial(串行GC)-复制
- ParNew(并行GC)-复制
- Parallel Scavenge(并行回收GC)-复制
- Serial Old(MSC)(串行GC)-标记-整理
- CMS(并发GC)-标记-清除
- Parallel Old(并行GC)--标记-整理
- G1(JDK1.7update14才可以正式商用)
说明:
- 1~3用于年轻代垃圾回收:年轻代的垃圾回收称为minor GC
- 4~6用于年老代垃圾回收(当然也可以用于方法区的回收):年老代的垃圾回收称为full GC
- G1独立完成"分代垃圾回收"
注意:并行与并发
- 并行:多条垃圾回收线程同时操作
- 并发:垃圾回收线程与用户线程一起操作
详细介绍一下cms垃圾回收器?
cms是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,这种垃圾回收器非常合适。在启动jvm的参数加上"-XX:+UseConcMarkSweepGC"来指定使用CMS垃圾回收器
cms使用的是标记-清除的算法实现的,所以在gc的时候会产生大量的内存碎片,当剩余内存不能满足程序运行要求的时,系统将会出现Concurrent Mode Failure,临时cms会采用Serial Old回收器进行垃圾清除,此时的性能将会被降低。
详细介绍一下G1垃圾回收器?
如何判断一个对象是否要被垃圾回收机制回收?
判断一个对象是否被回收通常会用两个算法:引用计数器算法,可达性分析算法
引用计数算法(已被淘汰)
引用计数算法:在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。
存在的问题:主流的Java
虚拟机里面都没有选用引用计数算法来管理内存,因为单纯的引用计数很难解决对象之间 相互循环引用 (A
引用B
,B
引用A
)的问题。
可达性分析算法(主流的算法)
当前主流的商用程序语言(Java
、C#
,上至古老的Lisp
)的内存管理子系统,都是通过可达性分析(Reachability Analysis
)算法来判定对象是否存活的。
哪些可以作为root?
由于Root采用栈方式存放变量和指针,所以如果是一个指针,它保存了堆内存里面的对象,但是自己又不存放在对象内存里面,那它就是一个Root
- 方法区中静态引用指向的对象;
- 方法区中常量引用指向的对象;
- JavaStack 中的引用的对象 (栈内存中引用的对象);
算法的基本思路:通过一系列称为 “GC Roots
” 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为 “引用链”(Reference Chain
),如果某个对象到 GCRoots
间没有任何引用链相连,或者用图论的话来说就是从GC Roots
到这个对象不可达时,则证明此对象是不可能再被使用的。
强引用:数组、对象 ;类似:Object obj = new Object()
只要存在强引用,垃圾收集器永远不会回收掉被引用的对象
弱引用:非必需对象,垃圾回收机制运行时,不管内存充足与否,都会回收
软引用:
描述一些还有用但并非必须的对象。在系统将要发生内存溢出异常之前,将会把这些对象列入回收范围之中进行第二次回收。如果这次回收之后还没有足够的内存——抛出内存异常
内存充足,不会回收
内存不足,回收
发生OOM后,如何排查和处理线上系统的OOM问题?
解决思路:在jvm里面设置几个参数,如果一旦jvm发生oom之后,就会导出一分内存快照,就会有当时的线上内存里的对象的一个情况,可以用mat这样的工具去进行分析
无非就是找出当时的时候占用内存最大的对象都是谁,找出来那些对象是在代码中哪些地方创建出来的,一般来说就是可能会对内存去做一个调优
JVM各内存区域存放内容
String str=new String("hello");
堆 存放的是new出来的对象 jvm中只有一个堆区 被所有的线程共享
栈 是变量str 每个栈中的数据私有的 其他栈不能访问。
方法区 是“hello”;被所有的线程共享,方法区包含所有的class static变量。
方法区:
类或者接口的访问修饰符public,abstract,final等,常量-池(字段,方法信息,静态变量,类型引用(class))
方法区是系统分配的一个内存逻辑区域,是一块所有线程共享的内存区域,用来存储类型信息(类型信息可以理解为类的描述信息(类的全限定名,访问修饰符,字段,方法等)),方法区的大小决定了系统可以包含多少个类,如果系统类太多,方法区内存不够会导致方法区溢出,虚拟机同样会抛出内存溢出信息。方法去特点:
1.方法区是线程安全的,由于所有的线程都共享方法区,所以方法区里的数据访问必须被设计成线程安全的。例如,假如同时有两个线程都企图访问方法区中的同一个类,而这个类还没有被装入jvm,那么只允许一个线程去装在它,而其他线程必须等待。
2.方法区的大小不必是固定的,jvm可根据应用需要动态调整,同时,方法区也不一定是连续的,方法区可以在一个堆(甚至是jvm自己的堆)中自由分配。
3.方法区也可被垃圾收集,当某个类不在被使用时,jvm将卸载这个类,进行垃圾收集。
程序计算器:
多线程的Java应用程序:为了让每个线程正常工作就提出了程序计数器(Programe Counter Register),每个线程都有自己的程序计数器这样当线程执行切换的时候就可以在上次执行的基础上继续执行,仅仅从一条线程线性执行的角度而言,代码是一条一条的往下执行的,这个时候就是程序计数器;JVM就是通过读取程序计数器的值来决定下一条需要执行的字节码指令,进而进行选择语句、循环、异常处理等;
A线程正在执行HelloWorld.class的第三十五行。这时候CPU时间片被B线程抢走了,当A线程重新被分配到时间片时,他怎么知道我的class运行到哪了?这时候他可以看程序计数器在哪个位置。
程序计数器作用不多说了,我个人感觉他是为了多线程而生的,单线程情况下完全不需要他。从案例中不难发现,程序计数器是每个线程独有的,并非线程共享的,所以是线程安全的!
虚拟机栈:
1、局部变量表: 用于存放方法参数和方法内部定义的局部变量。
2、操作数栈:操作数栈也成为操作栈,后入先出,操作数栈的最大深度也在编译的时候就已经确定,不会在运行期间动态变化。32位数据类型所占的栈容量为1,64位数据类型所占栈容量为2.
3、动态连接:class文件的常量池中有大量的符号引用,字节码中的方法调用指令就以常量池指向的方法的符号引用作为参数。这些符号引用一部分会在类加载阶段(解析阶段)或者第一次使用的时候就转化为直接引用,这种转化成为静态解析,另一部分在没一次运行期间转化为直接引用,这部分成为动态连接。
4、方法返回地址:
一个方法在执行时,只有两种方式退出这个方法:正常完成出口和异常完成出口
本地方法栈:
本地方法栈的功能和特点类似于虚拟机栈,均具有线程隔离的特点以及都能抛出StackOverflowError和OutOfMemoryError异常。
不同的是,本地方法栈服务的对象是JVM执行的native方法,而虚拟机栈服务的是JVM执行的java方法