目录
10.Minor GC和Full GC是什么意思,哪个更耗时
1.你们用什么工具监控JVM
jconsule, jvisualvm
jconsole和jvisualvm都是Java虚拟机的监控和诊断工具。
jconsole是JDK自带的一个监控工具,可以监控Java程序的内存、线程、类加载等信息,并且可以执行JMX MBean相关操作。jconsole提供了图形界面,使用方便,是Java程序开发和调试的常用工具,它通过连接到正在运行的Java应用程序JMX代理来获取信息。jconsole需要在目标Java应用程序上启动JMX代理,因此在应用程序中加入JMX代理的相关配置即可使用。
jvisualvm同样是JDK自带的监控和诊断工具,也具备丰富的功能,包括查看应用程序线程状态、堆内存使用情况,GC和类加载信息等。另外,jvisualvm还可以连接到目标应用程序进行远程监控,并且可以对应用程序进行分析和诊断。jvisualvm也提供了丰富的插件,可以扩展功能,使其更加强大。
总的来说,jconsole和jvisualvm都是非常优秀的Java虚拟机监控和诊断工具,使用起来都比较简单方便,能够方便地帮助我们定位问题并对Java应用程序进行监控和优化。
2.JVM内存模型,每个部分是干嘛的
-
堆:存放对象的区域,所有线程共享
-
虚拟机栈:对应一个方法,线程私有的,存放局部变量表,操作数栈,动态链接等等
-
本地方法栈:对应的是本地方法,在hotspot中虚拟机栈和本地方法栈是合为一体的
-
程序计数器:确定指令的执行顺序
-
方法区:存放虚拟机加载的类的信息,常量,静态变量等等,JDK1.8后,改为元空间
-
运行时常量池(Runtime Constant Pool):属于方法区的一部分,用于存储编译器生成的各种字面量和符号引用。在类加载后,Java虚拟机会将常量池中的符号引用替换为直接引用,这个过程叫做解析。
-
直接内存(Direct Memory):并不是JVM运行时数据区的一部分,它是在堆外分配的一块内存区域。JDK1.4中引入了NIO(New IO)库,它可以将FileChannel的数据修改映射到堆外内存上来执行,这样效率更高。直接内存的分配和释放不由Java虚拟机控制,而是由程序员自己负责管理。
3.类加载流程(JVM启动流程)
loading加载:class文件从磁盘加载到内存中
verification验证:校验class文件,包括字节码验证,元数据验证,符号引用验证等等
preparation准备:静态变量赋默认值,只有final会赋初始值
resolution解析:常量池中符号引用,转换成直接访问的地址
initializing初始化:静态变量赋初始值
4.JVM有哪些类加载器,用到什么设计模式,为什么这样设计
-
BootStrap ClassLoader 启动类加载器,加载<JAVA_HOME>\lib下的类
-
Extenstion ClassLoader 扩展类加载器,加载<JAVA_HOME>\lib\ext下的类
-
Application ClassLoader 应用程序类加载器,加载Classpath下的类
-
自定义类加载器
这里是用到了双亲委派模式,从上往下加载类,在这过程中只要上一级加载到了,下一级就不会加载了,这麽做的目的
-
不让我们轻易覆盖系统提供功能
-
也要让我们扩展我们功能。
5.什么是栈帧,它由什么组成
栈帧(方法执行形成栈帧):栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,线程私有。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用至执行完成的过程,都对应着一个栈帧在虚拟机栈里从入栈到出栈的过程,栈帧随着方法调用而创建,随着方法结束而销毁
6.JVM1.8和之前有什么区别
区别一(方法区)
1.8版本之后,使用元数据区实现了方法区,之前是使用永久代来实现方法区,大小是启动时固定好的;
元空间不在虚拟机中,而是使用本地内存,并且大小可以自动增长,减少了OOM(内存溢出)的几率;
区别二(堆区)
Java7之后运行时常量池从方法区移到了这里,为Java8移出永久代做好准备;
7.垃圾标记算法有哪些
垃圾标记算法有:引用计数和可达性算法
-
引用计数 : 给每一个对象添加一个引用计数器,每当
有一个地方引用它时,计数器值加1
;每当有一个地方不再引用它时,计数器值减1
,这样只要计数器的值不为0,就说明还有地方引用它,它就不是无用的对象. 这种算法的问题是当某些对象之间互相引用时,无法判断出这些对象是否已死 -
GC Roots :找到一个对象作为 CG Root , 当一个对象到GC Roots没有任何引用链相连(GC Roots到这个对象不可达)时,就说明此对象是不可用的
8.垃圾回收算法有哪些
-
标记清除算法 :分为标记和清除两个阶段,首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象 ;缺点:标记和清除两个过程效率都不高;标记清除之后会产生大量不连续的内存碎片。
-
复制算法 :把内存分为大小相等的两块,每次存储只用其中一块,当这一块用完了,就把存活的对象全部复制到另一块上,同时把使用过的这块内存空间全部清理掉,往复循环 ,缺点:实际可使用的内存空间缩小为原来的一半,比较适合
-
标记整理算法 :先对可用的对象进行标记,然后所有被标记的对象向一段移动,最后清除可用对象边界以外的内存
-
分代收集算法 :把堆内存分为
新生代和老年代
,新生代又分为Eden区、From Survivor和To Survivor。一般新生代中的对象基本上都是朝生夕灭的,每次只有少量对象存活,因此新生代采用复制算法
,只需要复制那些少量存活的对象就可以完成垃圾收集;老年代中的对象存活率较高,就采用标记-清除和标记-整理算法
来进行回收。
9.垃圾回收器有哪些,有什么区别,你们用的哪个
-
新生代:Serial :一款用于
新生代的单线程收集器,采用复制算法进行垃圾收集
。Serial进行垃圾收集时,不仅只用一条线程执行垃圾收集工作,它在收集的同时,所有的用户线程必须暂停(Stop The World -
新生代:ParNew : ParNew就是一个Serial的多线程版本`,其它与Serial并无区别。ParNew在单核CPU环境并不会比Serial收集器达到更好的效果,它默认开启的收集线程数和CPU数量一致,可以通过-XX:ParallelGCThreads来设置垃圾收集的线程数。
-
新生代:Parallel Scavenge(掌握) Parallel Scavenge也是一款用于新生代的
多线程收集器
,与ParNew的不同之处是,ParNew的目标是尽可能缩短垃圾收集时用户线程的停顿时间,Parallel Scavenge的目标是达到一个可控制的吞吐量
.Parallel Old收集器以多线程,采用标记整理算法进行垃圾收集工作。 -
老年代:Serial Old ,Serial Old收集器是Serial的老年代版本,同样是一个单线程收集器,采用标记-整理算法。
-
老年代CMS收集器是一种以最短回收停顿时间为目标的收集器,以“最短用户线程停顿时间”著称。整个垃圾收集过程分为4个步骤
-
初始标记:标记一下GC Roots能直接关联到的对象,速度较快
-
并发标记:进行GC Roots Tracing,标记出全部的垃圾对象,耗时较长
-
重新标记:修正并发标记阶段引用户程序继续运行而导致变化的对象的标记记录,耗时较短
-
并发清除:
用标记-清除算法清除垃圾对象
,耗时较长
整个过程耗时最长的并发标记和并发清除都是和用户线程一起工作,所以从总体上来说,
CMS收集器垃圾收集可以看做是和用户线程并发执行的。
-
-
老年代:Parallel Old ,Parallel Old收集器是Parallel Scavenge的老年代版本,是一个
多线程收集器,采用标记-整理算法。可以与Parallel Scavenge收集器搭配,可以充分利用多核CPU的计算能力
。 -
堆收集:G1 收集器, G1 收集器是jdk1.7才正式引用的商用收集器,现在已经成为
jdk1.9默认的收集器
。前面几款收集器收集的范围都是新生代或者老年代,G1进行垃圾收集的范围是整个堆内存
,它采用“化整为零”的思路,把整个堆内存划分为多个大小相等的独立区域(Region)
在每个Region中,都有一个Remembered Set来实时记录该区域内的引用类型数据与其他区域数据的引用关系(在前面的几款分代收集中,新生代、老年代中也有一个Remembered Set来实时记录与其他区域的引用关系),在
标记时直接参考这些引用关系就可以知道这些对象是否应该被清除,而不用扫描全堆的数据
Jdk1.7.18新生代使用Parallel Scavenge,老年代使用Parallel Old
我们用的是gc
10.Minor GC和Full GC是什么意思,哪个更耗时
Minor GC
新生代的回收称为Minor GC,新生代的回收一般回收很快,采用复制算法,造成的暂停时间很短
Full GC
而Full GC一般是老年代的回收,并伴随至少一次的Minor GC,新生代和老年代都回收,而老年代采用标记-整理算法
,这种GC每次都比较慢
,造成的暂停时间比较长
,通常是Minor GC时间的10倍以上。
所以很明显,我们需要尽量通过Minor GC来回收内存,而尽量少的触发Full GC
。毕竟系统运行一会儿就要因为GC卡住一段时间,再加上其他的同步阻塞,整个系统给人的感觉就是又卡又慢。
11.有过JVM调优经验嘛?怎么调的
是的,我有一些JVM调优经验,主要包括以下几个方面:
1. 初始堆大小和最大堆大小的设置:如果应用程序使用的是默认设置,则JVM会自动计算初始堆大小和最大堆大小。但是在一些特定的情况下,需要手动设置初始堆大小和最大堆大小,以避免出现内存溢出等问题。
2. 新生代大小的设置:新生代是比较重要的内存区域,一般来说,需要将新生代的大小设置为整个Java堆的1/3到1/4之间,以便更好地满足对象的创建和回收等操作。
3. 堆内存分区比重的设置:根据应用程序实际的内存使用情况和性能需求,可以根据需要设置老年代、幸存区和新生代等内存分区的比重,以达到更好的内存利用和更好的性能表现。
4. 垃圾收集器的选择:垃圾收集器的性能和特点有所不同,根据实际情况选择合适的垃圾收集器,可以有效地提高程序的性能。
5. 堆外内存的管理:一般情况下,JVM中的堆外内存占用量比较小,但在一些特殊情况下,应用程序可能会占用较多的堆外内存,这时需要进行相应的管理和调优。
在调优JVM时,可以使用一些专业的JVM调优工具进行监控和分析,在WeTab中,我们使用的是VisualVM和JMX监控工具。同时,优化JVM也需要结合实际的应用场景和需要进行综合分析和调整。
12.堆大小怎么设置,栈的大小怎么设置
-Xms : 初始堆,1/64 物理内存
-Xmx : 最大堆,1/4物理内存
-Xmn :新生代大小
-Xss : 栈大小
13.OOM怎么排查(内存溢出)
1.设置堆大小,以及捕获jvm日志
2.使用Java Visual VM 分析日志
当Java应用程序出现OOM(Out Of Memory)错误时,需要进行如下的排查步骤:
1. 查看JVM错误日志文件:当Java应用程序出现OOM错误时,JVM会在错误日志中记录相关的信息,将这些信息作为排查的第一步。
2. 使用堆转储工具:使用Java的Jmap命令或者第三方工具如MAT(Memory Analyzer Tool)等生成Java堆转储文件,以查看Java应用程序中的对象数量、内存分配情况、对象生命周期等。
3. 分析堆转储文件:使用堆转储工具打开生成的堆转储文件,观察内存中的对象占用情况等信息,找出可能导致OOM的原因。
4. 检查代码中的问题:分析堆转储文件后,可以根据发现的问题点检查应用程序代码,查看是否存在内存泄漏、大对象、循环引用等问题。
5. 尝试调整JVM参数:在分析完代码后,可以根据具体情况增加JVM内存或者调整GC相关的参数,例如增大堆大小、调整新生代比例或者更换不同的GC算法等,以优化应用程序的内存使用情况。
需要注意的是,当检测到OOM错误时,建议避免立即重启应用程序,这样可能会丢失一些有用的信息。可以使用上述步骤进行排查,找出问题的根源,然后针对性地进行修复。