1. jvm的分区有哪一些,每个分区又放了哪些东西?
运行时常量池的位置:
参考文章:https://www.cnblogs.com/better-farther-world2099/articles/13889075.html
常见:栈,堆,方法区。
栈:分为java虚拟机栈和本地方法栈,我们经常接触到的是java虚拟机栈,是线程私有的。每个方法在执行时,都会生成一个栈帧用来存放局部变量等信息。方法开始时栈帧会进入java虚拟机栈,结束时栈帧会从java虚拟机栈中出来。局部变量包括什么呢?基本数据类型(byte,short,int,long,float,double,boolean)和对象的引用,它的作用域只是在这个方法的内部。
java虚拟机栈中可能会抛出的异常?经测试,栈的深度在10800左右时,会提示stackOverFlowError。
stackOverFlowError, 线程请求的栈深度大于允许的深度。
OutOfMemoryError,栈可以动态扩容,但当无法申请到更多的内存的时候会抛出这个异常。
堆:指java堆,是java虚拟机中占用内存最大的,也是所有线程共享的一个内存区域,存放的是几乎所有对象的对象实例。
内存不够时会抛出outOfMemoryError的异常
方法区:也是所有线程共享的一个区域,存放的信息:类信息,常量,静态变量,即时编译器编译的代码缓存
常量池:方法区中的一部分,存放各种字面量和符号引用
其它次要的内存空间:
- 程序计数器:每个线程都有自己独立的程序计数器,负责记录执行到的字节码行号。
- 本地方法栈:为执行本地方法提供服务。
补充:
上面说的是java虚拟机规范中的一些内存区域。除此之外还有个叫“直接内存”的空间,它不属于java虚拟机规范的内存区域,但是它又经常被使用到。
2. 堆里面的分区:Eden,survival (from+ to),老年代,各自的特点。
eden和survival区,属于新生代区,这个区域的对象的存活时间较短,存活下来的对象较少,适合使用标记复制算法来进行垃圾回收。
老年代,这个区域的对象的存活时间较长,时候采用标记-整理算法进行垃圾回收。
3. 对象创建方法,对象的内存分配,对象的访问定位
创建方法:语言上变现为一个new的过程,而虚拟机则是在遇到一个new的指令时,会去常量池中定位该类的符号引用并检查该类是否完成了类的加载过程,如果没有则需要经历过响应的类加载过程。
内存分配:需要关注两个点:1是分配多大的内存给对象;2.如果找到一块空闲的内存给对象。关于第一个问题,在类完成加载后便可以确定。第二个问题,不同的垃圾回收器会使用不同的内存分配算法:serial,ParNew使用指针碰撞算法。cms使用‘空闲列表’的方式来给对象分配内存。
4. GC的了两种判定
引用计数器:有多少处地方引用该对象,该对象的引用计数就为几,当计数为0时,表明该对象不再被引用了,即可进行垃圾回收。python的内存管理机制,存在循环使用的问题。
可达性分析:在该算法中定义了一些GC roots(这些GC roots可以是java栈中引用的对象或,方法区中静态属性引用的对象,方法区中常量引用的对象),当一个对象没有一个引用连接到GC roots, 则证明是不可用的,则被判定为可回收对象。但是一个对象如果要真正被回收需要经历两个过程。
5. SafePoint 是什么
安全点是一些位置,在这些位置上程序会停顿下来执行GC。常见的产生安全点的指令:方法调用,循环跳转,异常跳转等
6.GC 的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用 在什么地方,如果让你优化收集方法,有什么思路?
标记清除:像它的名词一样,先标记需要清除的对象,然后回收其占用的内存。缺点:会产生大量的内存碎片,优点:垃圾回收的速度快。优化点:定期整合内存碎片
标记复制:将内存一分为二,进行垃圾回收时,将存活下来的对象从内存块A中移动到内存块B, 然后将内存块中的对象全部清除。缺点:内存的利用率不高,需要空闲一大块内存来保存存活下来的对象。该策略常用于新生代。
标记整理:将需要清除的对象移动到内存的一端,然后批量清除。缺点:如果需要清除的对象过多,需要移动的对象就会很多,也很耗性能。
综上:新生代,因为其大部分对象朝生夕灭,每次垃圾回收之后存活少数对象的特征,适合用复制算法。改进方向:如何确定存活下来的对象的大小,这样就只需要预留少量的内存来保存存活对象的,不会浪费太多内存。老年进行垃圾回收是只会清除少量的对象,适合使用标记整理算法。从整个对像迁移的趋势来看,使用的是标记复制的思想。而如果单从老年代来看,因为老年代这个区域上的对象不能迁移到其它的地方去。所以也只使用标记整理的思想。
7. GC 收集器有哪些?CMS 收集器与 G1 收集器的特点。
serial:单线程,用于新生代
parNew:多线程,用户新生代
paralle Scavenge:多线程,关注吞吐量,可设定自适应策略(自动调节eden区和survival区的比例,晋升老年代的阈值),用户新生代
serial Old:单线程,老年代垃圾回收器,和serial配合使用。
parallel Old:多线程, 老年代垃圾回收器,和parallel Scavenge配合使用
CMS:老年代垃圾回收器,追求最短的垃圾回收时间。使用标记清除算法可以和serial或parNew配合使用。
G1: 关注点最小化垃圾回收时间,允许用户自己设置垃圾回收的时间;保留分带分区的概念,但是颗粒度会更小,每个区都被分成多块大小相同的子区;垃圾回收时不会对全带全区的内存进行垃圾回收,会先回收优先级较高的内存块。
java8默认使用的:parallel Scanvege+parallel Old
jvm GC算法类型&垃圾收集器种类详解一条龙_雨剑yyy的博客-CSDN博客
8.CMS使用的垃圾回收机制?运作过程?
最 短停顿时间为目标。整个过程分4步:
初始标记:标记GC Roots可以直接关联的对象。
并发标记,进行GC Roots Tracing的过程
重新标记,修正并发标记期间,因程序继续运行而导致标记发生变化的对象。
并发清除,清除标记对象。
缺点:占用cpu资源,无法处理浮动垃圾(标记过程中产生的垃圾,本次垃圾回收无法处理,需要等到下次垃圾回收才可处理),产生内存碎片(当内存中碎片过多,无法存放大对象时,可开启内存整理,这个过程无法并发,会引起停顿)
cms相比paraller Old。
paraller Old关注吞吐量,只是多线程进行垃圾回收,会stop world,不与用户线程并行。可能会造成单次stop world的时间过长。cms就不会,因为它的stopwold的时间比较多,只会在初始标记和最终标记的时候stop world,相比并发标记阶段(分析gc root的引用链)处理的数据较小
GC当中吞吐量和暂停时间的区别是:吞吐量就是CPU 用于运行用户代码的时间与CPU 总消耗时间的比值,即吞吐量= 运行用户代码时间/(运行用户代码时间+ 垃圾收集时间)
9. Minor GC 与 Full GC 分别在什么时候发生?
minorGC又叫做,新生代GC, eden区内存不够用的时候会触发minorGC。
Full GC,又叫做major GC或者说是老年代GC, 当有对象将要从新生代迁移到老年代的时候,会先检测老年代中是否有连续的内存可以存下这个对象,如果没有将会触发Full GC。
10. 类加载的几个过程
什么时候会执行加载的动作?什么时候会执行初始化的动作?
加载动作的执行时间,jvm规范未做强制约束;
而初始化动作必须在下面5种情况下才能进行(前提是加载,校验,准备,解析动作已经完成):
a. 遇到new, getstatic, putstatic, invokestatic字节码指令,并且此时类没有进行初始化则会完成对应的初始化操作
b. 对类进行反射调用的时候,并且此时类没有进行初始化则会完成对应的初始化操作
c. 初始化一个类,必定会先初始化它的父类,若此时父类没有完成初始化动作则会完成对应的初始化操作
d. 启动虚拟机时,会初始化负责执行的主类(含main方法的那个类)
e. jdk 1.7的动态语言中, 如果一个java.lang.invoke.MethodHandle解析结构的方法句柄,这个方法句柄没有进行初始化的话,就需要先进行初始化
1. 加载:将.class文件通过二进制字节流的形式加载到内存当中,并生成对应的java.lang.Class对象,作为程序访问该类信息的入口。非数组类的加载过程,由相应的类加载器去完成。数组类本身不通过类加载器创建,它由java虚拟机直接创建,但是数组中的类相应的类加载器去完成
2. 验证:验证.class的字节流是否符合java虚拟机的要求,包括格式校验(魔数cofebase开头,java版本号,常量池中是否有不支持的类型等校验),元数据校验(类级别的信息校验,如继承关系;非抽象类是否含有抽象方法;overreide的方法,子类的参数和返回值是否和父类的相同),字节码校验(类中方法体中代码的校验,保证方法体中的字节码可以正常的进行),符号引用校验(主要是些匹配性的校验,校验类,属性,方法体的符号引用是否可以对应上具体的类,属性,方法)
3.准备:给静态变量分配内存空间并初始化为0。
4.解析:将常量池中的符号引用转化为直接引用的过程,包括类的解析(加载,验证的过程),属性解析(先找本类是否有这个字段,如果有则返回直接引用;若没有则去父类或者父接口中去寻找是否有这字段,若没有,还会向上层去寻找),类的方法解析(和属性解析的过程相同,逐层寻找所在的类,返回直接引用)。符号引用:一组描述目标的符号;直接引用:直接指向目标的指针。
5.初始化:执行类构造器clinit方法,初始化静态变量的值,执行static代码块中的内容。(clinit方法是编译器自动收集的类中所有的类变量的赋值动作和静态代码块的中的语句合并生成的)
clinit方法执行的过程是做了同步操作的(就是加锁操作),当第一个线程执行clinit方法的时候,其他的线程会处于阻塞等待的状态。
6.使用
7.卸载
10. 类加载器有哪一些?
类加载器的作用:类加载器负责加载 Java 类的字节代码到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class
类的一个实例。基本上所有的类加载器都是 java.lang.ClassLoader
类的一个实例。下面详细介绍这个 Java 类。java.lang.ClassLoader
类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个 Java 类,即 java.lang.Class
类的一个实例。
1. 启动类加载器:BootStrap ClassLoader, C++语言实现,是java虚拟机的一部分,加载java的核心库,将JAVA_HOME/lib路径中的加载到虚拟机内存。
2.扩展类加载器:加载java的扩展库,加载JAVA_HOME/lib/ext中的类库
3. 应用程序类加载器:java语言实现,独立于虚拟机外部,继承抽象类java.lang.ClassLoader,加载用户类路径(classPath)上指定的类。默认的类加载器。
11. 什么是双亲委派模型?
双亲委派模型,描述了这样一种结构,最顶层是启动类加载器,然后是扩展类加载器,再是应用程序类加载器。当然也可以自定义加载器,这种加载器是要接在应用程序加载器的后面的。上述描述的接口是以组合的方式实现,而不是以继承的方式实现。这保证了一个类的类加载器只会有一个。
双亲委派模型的工作过程:当一个类加载器接收到加载请求的时候,不会先尝试自己去加载这个类,而是将这个请求传给父加载器,让父加载器先加载,每层都是如此。只有当父加载器反馈不能加载时,子加载器才会去尝试加载。
3次大规模破坏双亲委派模型的场景?
a. jdk 1.2 引入双亲委派模型之前
b. java中涉及spi(service provider interface)的加载动作,会使用父类加载器去请求子类加载器完成一些加载的动作。不符合双亲委派模型的原则。
c. OSGi环境下,不在是双亲委派模型,而是更复杂的树状结构。
12. 了解G1垃圾回收器吗?请介绍下。
特点:
a. 并发和并行,java线程可以和GC线程一起运行。
b. 分代收集,
c.空间整合,采用标记整理算法进行内存管理,不会产生垃圾碎片
d.可预测停顿,开发者可以手动指定垃圾回收的最大时间。
内存模型:保留新生代和老年代的概念,但是他们之间没有物理隔离,G1和其它垃圾回收器的内存格局不太一样,它将java堆划分成大小相等的独立区域region,新生代和老年代就是由这些region组成的。
过程:
初始标记:分析可以和GC roots直接关联上的对象。
并发标记:进行可达性分析,与用户线程并发执行。
最终标记:分析并发标记过程中,因为程序运行产生的标记变化的对象,这个变化将被记录到remember Set当中。
筛选回收:根据各个region的回收价值和成本进行排序,再根据用户期望的垃圾回收时间,制定垃圾回收计划。
这个过程描述和cms的过程描述,基本没差别,换了个名称而已。
13. 在进行GC roots分析时,需要分析老年代对象是否引用了新生代对象,这样的话进行minor GC的时候,要对老年代进行全堆扫描才能知道,会很耗费性能?怎么解决这个问题?
G1的解决方案,使用remember set。每个region都维护了一个remember Set,它维护其它region的信息;这些region引用了改region中的对象。这样不需要进行全区扫描也知道,一个region中的对象是否被其它region引用。
14. G1是如何实现可预测停顿的?
通过维护优先列表的方式,优先列表中各个region按照回收价值进行排序(价值由收回消耗时间和回收的空间决定)。
15. 如何解决内存碎片的问题?
1) 如果你使用了CMS进行垃圾回收,怎么处理垃圾碎片的问题?
官方方案:
- 增大Xmx或者减少Xmn
- 在应用访问量最低的时候,在程序中主动调用System.gc(),比如每天凌晨。
- 在应用启动并完成所有初始化工作后,主动调用System.gc(),它可以将初始化的数据压缩到一个单独的chunk中,以腾出更多的连续内存空间给新生代晋升使用。
- 降低-XX:CMSInitiatingOccupancyFraction参数以提早执行CMSGC动作,虽然CMSGC不会进行内存碎片的压缩整理,但它会合并老生代中相邻的free空间。这样就可以容纳更多的新生代晋升行为
2)常规的内存碎片解决方案?
使用分页的方式,解决内存碎片。比如说:每页的内存大小是2,现在要给一个内存大小为3的对象申请内存。那么就必须申请两页,这两页的物理内存地址可以是不连续的。这样可以解决内存碎片过多导致不能存放大对象的问题。
16. 如何解决同时存在的对象创建和对象回收问题?
在CMS和G1并发垃圾回收器中,用户线程是可以和GC线程同时进行的。
17. 如何理解内存泄漏问题?有哪些情况会导致内存泄露?如何解决?
理解:对象在业务逻辑上已经不被使用了,但是因为它和GC Roots可以直接或者间接的关联上,又不会被垃圾回收器回收。
场景:threadLocalMap, 非静态内部类创建静态实例,单例持有外部引用,
方法:手动清空,像list中的clean方法,threadLocal中的remove方法。
18. java内存模型,运行时数据区
Java内存区域(运行时数据区域)和内存模型(JMM) - czwbig - 博客园
程序计数器的组作用:每个线程都会有个程序计数器,记录当前线程执行到的字节码行号。
本地方法栈:本地方法栈是为native方法提供服务的,如System.currentTimeMilli(), unsafe类中的绝大多数方法都是naive的。
本地内存:并不是java运行时区的一部分,java NIO会使用到这块内存,它和java的堆的联系:java堆可通过DirectByteBuffer对象操作直接内存。
19. 运行时的栈帧中包含哪些东西?
局部变量表:存放方法的参数和方法内的局部变量
操作数栈:执行字节码指令的地方,拿c = a + b举例, 假设a,b,c都是int类型, 首先通过字节码指令将a的值压入栈中,然后将b的值压入栈中。再调用add的指令将ab向加的结果压入栈中,然后再将结果取出赋值给局部变量表中的c变量。可以参考文章:三大Java 虚拟机垃圾回收机制的比较(HotSpot, JRockit, IBM JVM) - 巷尾的酒吧 - BlogJava
动态链接:运行时该栈帧所属方法的引用。
方法返回地址:指的是方法被调用的位置,方法正常退出时可以使用调用者的计数器作为返回地址。异常退出时,需要通过异常处理器确定返回位置。
方法出栈时会有什么操作?
恢复上层方法的局部变量表和操作数栈,如果有返回值的话,将返回值压入调用者栈帧中的操作数栈中,调整计数器的值,指向调用方法的后一条指令。
20. java程序性能问题排查,主要考察jdk自带命令,没事可以都看一遍
21. class类文件的基本特性?
a. 以8位字节为基础单位的二进制流
b..class文件中没有任何的分割符,所以无论是顺序还是数量都是被严格定义的,各个位置上字节代表的含义都是不能改变的。
22. class类文件的结构?
a.魔数,CAFEBASE, 作用:确定是否是虚拟机可以接受的class文件
b.次版本号,主版本号:jdk 1.8对应的版本号是52
c.常量池,class文件中的资源仓库,主要存放两大类常量:字面量和符号引用。字面量接近java语言中常量的概念,如文本字符串,声明为final类型的常量值。符号引用:属于编译原理方面的概念,类和接口的全限定名,字段的名称和描述符,方法的名称和描述符。如果是字符串的字面量会用String类型表示;如果是类,方法,变量的符号引用会用utf-8进行表示,utf-8的最大长度是类名,方法名,字段名的最大长度;如果是类或者接口的引用,会用class进行表示。
d. 访问标记:标识类或者接口层级的访问信息,一般会是ACC_PUBLIC(public修饰的类), ACC_SUPER(是否允许使用invokespecial字节码指令的新语义)。还有些ACC_ABSTRACT标记抽象类,ACC_INTERFACE标记接口,ACC_ENUM标记枚举。
f. 属性表集合:存放方法编译后产生的字节码,
下面简单了解下常用的字节码的含义
数据类型标记:l 表示long,s代表short,b代表byte,c代表char, d代表double, a代表reference
但是byte,short,char没有对应的load,store指令,他们会被转换成int类型,使用int类型相关的操作指令
加载指令:将数据从临时变量表中加载到操作数栈中
存储指令:将数据从操作数栈中保存到临时变量表中
运算指令:操纵位于操作数栈中的数,加减乘除,求余,与或等
类型转换指令:用不同的两种数据类型之间的相互转换
对象创建和访问指令:new 创建对象,newarray 创建数组等
控制转移指令:控制指令执行的位置,if和switch相关
方法调用指令:各种invoke指令,最常见的是invokevirtual,调用对象实例的方法
方法返回指令:return
异常抛出指令:athrow
同步指令:monitorenter和monitorexit
23. 解释下java中的方法调用
方法调用不是方法执行,方法调用的目的是确定方法调用的版本。Class文件中只是存放了方法的符号引用,而将这个符号引用转为直接引用可能发生在类加载期间也可能发生在运行期间。
24. 什么方法是在类加载时确定,什么方法又是在程序运行期间确定呢?
类加载时确定的方法:静态方法和类私有的方法,这两者不能通过继承或是重写的方式使得方法又其它的版本。
运行期间确定的方法:实例类的共有方法。
25. 方法的字节码调用指令有哪些?
invokestatic:调用静态方法
invokespecial:调用类的构造方法,私有方法,父类方法
invokevirtual:调用所有的虚方法
invokeinterface:调用接口方法,运行时确定一个此接口的实现类
invokedynamic:运行时解析调用的符号引用的方法
26. 谈谈重载(overload)的方法是怎样确定调用的方法的?
方法的重载和方法名及参数列表相关,与返回值无关。
从方法分派的角度说:通过参数的静态类型而不是实际类型进行分派。有时也会通过类型转换的方式找到合适的方法。
27.谈谈重载(override)是怎么调用方法的?
动态分配,使用对象的真实类型,调用对应的方法。而不是像重载一样使用的对应的静态类型。
28. 什么是方法的宗量?
方法的接受者和方法的参数称为方法的宗量。
29. 什么是单分派和多分派?
在java中,编译期间方法名和参数已经确定,剩下决定方法调用的只会是对象的实际类型,所以java语言属于单分派类型。
30. 虚拟机时如何做到动态分派的呢?
虚拟方法表,在方法区中建立一个虚拟方法表,使用它来代替元数据提高查找性能。
31. invokedynamic语言的用处?
它不是给java语言的使用者使用的,而是给java虚拟机上的其他动态语言使用的。
什么是编译执行?什么是解释执行?
3分钟搞懂什么是编译执行和解释执行《轻松搞定大厂面试》 - 腾讯云开发者社区-腾讯云
32. 解释下java中的解释执行?
编译过程:词法分析 -> 语法分析 -> 抽象语法树 -> 指令流 -> 解释器 -> 解释执行
java中的解释执行:指令流(基于栈的指令集组成) + 解释器(计数器+临时变量表+操作数栈)
词法分析:将源代码的字符流转成标记集合,标记是编译过程中的最小元素,如关键字,字面量,变量名,运算符
语法分析:分析语法,得知代码逻辑,构成对应的语法树
符号表:词法分析和语法分析后,会进行符号填充的过程。符号表:符号地址和符号信息构成。用于语义检测和产生中间代码。
解释器执行的是字节码指令流,编译器执行的是本地机器码。
33. 解释下java中的编译执行(也可以说是运行时编译)?
目的:对于某些执行特别频繁的方法或者代码块,将其编译成与本地相关的机器码,进行各种层次的优化。
hotspot中有两个即时编译器,分别是client compiler(c1 编译器)和server compiler(c2编译器)
虚拟机使用何种即时编译器可以自己指定。
C1:将字节码编译成本地机器码,进行些简单可靠的优化。
C2:将字节码编译成本地机器码,进行些复杂的,耗时较长的,有时甚至是不可靠的优化。
触发条件:
被多次调用的方法;
被多次调用的循环体:
如何统计调用次数:
使用基于计数器的热点探测
方法执行流程:
C1的编译过程:
33. 什么是方法内联?
简单通俗的讲就是把方法内部调用的其它方法的逻辑,嵌入到自身的方法中去,变成自身的一部分,之后不再调用该方法,从而节省调用函数带来的额外开支
从效果上看,它是将方法中的代码复制到方法调用处;
公共表达书消失是什么优化?
如int a = b + c + n*m + m*n; , 其中n*m和m*n的含义其实是一样的。所以可以优化成int a = b + c + E + E
避免重复计算
34. 什么是数组边界检查消除?
我们知道访问数组时如果访问数组下标超过了数组的边界会报数组越界异常,这时因为java进行了隐式的数组越界检查的缘故。想象下循环体中不断的通过下标访问数组中的值,每次访问都会检验边界是否超。出。但其实很多情况下我们知道不会超出,因为我们已经规定了循环的范围0<= i < arr.length。
35. 如果发生了OOM,需要怎样去定位问题?
使用jmap或者是java visualVM工具得到堆的dump日志,通过dump日志可以找到实例大小占比最高的类,这些是最优可能引发OOM的类。然后再查看对应的代码,是静态集合变量引起的呢还是一次插查询数据量太大引起的。
操作方法:
1. 添加jvm启动参数-XX:+HeapDumpOnOutOfMemoryError,这样在jvm内存溢出时会自动生成dump日志,是.hprof格式的文件
2. 启动visualVM, 导入.hprof类型文件,可以分析下内存溢出时占比对多的类是哪个。使用JProfiler工具,可以使用引用分析效果会更好。
36. 如果发生了线程死锁,需要怎么去定位问题?
使用java visualVM 可以自动检测到相互死锁的线程 -- 如果是使用linux服务器,这个工具可以使用不了。
这个时候可以考虑使用:jps -l 指令找到目标进程; 然后在使用jstack 9816 , 9816是目标进程的端口号,需要更具实际情况进行修改;
补充:当使用云资源服务器时,jps, jstack,jstat等工具去监控jvm时是十分有用处的。
jstat: 用于统计内存,垃圾回收等信息; 例如:jstat -gc 12382 1000 10 或者 jstat -gcutil 12382 1000 10
jps -l:找java进程主类的进程
jstack:查看某个Java进程内的线程堆栈信息。如:jstack 87285 -l ;对87285线程进行死锁检测;查看指定线程,jstack -l 1 | grep http-nio-18080-exec-1 -A 10
jmap:查看进程的内存信息, 甚至支持dump;jmap -heap pid;
jmap -dump:live,format=b,file=gc.dhump pid 打印存活对像
内存溢出时生成dump文件做法:可以统计到对象占用堆内存的百分比JVM性能调优监控工具hprof使用详解_格色情调1984的博客-CSDN博客
jhat: 用来分析jmap导出的内存信息,云服务上不好用。
查看JVM运行参数jinfo命令-腾讯云开发者社区-腾讯云jstat分析JVM的使用情况和查看full gc频率_jstat gct_Crystalqy的博客-CSDN博客查看JVM运行参数jinfo命令-腾讯云开发者社区-腾讯云
特别注意:
jmap,jstack,jstat指令会触发stop world ,如果服务正在处理大量请求,可能会大面积导致上游接口超时。
gc问题排查:
1. 搜索gc.log日志中是否具有‘Full GC’字样,有则表明发生了full gc。
2. 查看日志中“ concurrent mark-sweep generation total”字样,看看老年代的变化情况。
3. 不要在生产环境随便操作jstack,jmap,jstat指令:由于 jstack,jmap 和 jstat 等命令,也就是 Signal Dispatcher 线程要处理的大部分命令,都会导致 Stop the world:这种命令都需要采集堆栈信息,所以需要所有线程进入 Safepoint 并暂停。
特别需要关注老年代的成长速度,如果超过了old gc的阈值,触发了MajorGC,会增加请求的响应时间。
4. 打印堆中对象占用内存情况
jmap -histo 13988 > ./log.txt
5. 如何寻找cpu消耗比较高的线程
JVM找出占用CPU最高的线程_jvm显示消耗最大的程序_notsaltedfish的博客-CSDN博客
37. 源码文件(.java文件)到代码执行的过程?
38. cpu如何执行机器码?
图解Google V8 -- 机器代码:二进制机器码究竟是如何被CPU执行的? - 掘金3
39. jvm执行器执行代码的过程?
cms
https://www.cnblogs.com/zhangxiaoguang/p/5792468.html
40. 如何定位消耗cpu过高的线程?
41.导出dump文件
jmap -dump:format=b,file=dump 1
tar -czf dump.tar dump
42. 查看某jvm参数
jinfo -flag MaxHeapSize 23832