面试必问:JVM原理和调优(附面试题)_温文尔雅的清欢渡的博客-程序员宅基地_jvm原理及性能调优

技术标签: jvm  03 | 面试专栏  

在这里插入图片描述

一、详解JVM内存模型

JVM有本地方法栈、虚拟机栈、程序计数器、堆、方法区。
JVM内存分为共享区(可以被所有方法(线程)直接访问)和私有区(对线程来说是私有的,其他线程无法直接访问)。
在共享区里包含着堆和方法区,在私有区里包含着程序计数器、虚拟机栈和本地方法栈。

程序计数器PC:是一个行号计数器,程序在进行跳转时,我们要记住跳转的行号,它方便我们的程序进行还原。
虚拟机栈:包含了Java方法执行时的状态,每一个Java方法都会在虚拟机栈里面创建一个栈帧,里面存放局部变量表、操作数栈、动态链接、方法出口等。
本地方法栈:跟虚拟机栈类型,在用于调用操作系统的底层方法时才会创建栈帧。
堆:用来保存着Java程序运行时的变量,比如new的对象。
方法区:则保存着静态的东西,比如静态变量、常量、类的信息、方法的申明等。

面试题必问:Java虚拟机内存模型

二、JVM中一次完整的GC流程是怎样的

在这里插入图片描述
Java堆 = 老年代 + 新生代
新生代 = Eden + S0 + S1

当 Eden 区的空间满了, Java虚拟机会触发一次 Minor GC,以收集新生代的垃圾,存活下来的对象,则会转移到 Survivor区。
大对象(需要大量连续内存空间的Java对象,如那种很长的字符串)直接进入老年态;
如果对象在Eden出生,并经过第一次Minor GC后仍然存活,并且被Survivor容纳的话,年龄设为1,每熬过一次Minor GC,年龄+1,若年龄超过一定限制(15),则被晋升到老年态。即长期存活的对象进入老年态。
老年代满了而无法容纳更多的对象,Minor GC 之后通常就会进行Full GC,Full GC 清理整个内存堆 – 包括年轻代和老年代老年代。
Major GC 发生在老年代的GC,清理老年区,经常会伴随至少一次Minor GC,比Minor GC慢10倍以上。

三、GC垃圾回收的算法有哪些

引用计数算法
跟踪回收算法
压缩回收算法
复制回收算法
按代回收算法
浅析Java的垃圾回收机制(GC)和五种常用的垃圾回收算法

四、简单说说你了解的类加载器

类加载器 就是根据指定全限定名称将class文件加载到JVM内存,转为Class对象。

启动类加载器(Bootstrap ClassLoader):由C++语言实现(针对HotSpot),负责将存放在<JAVA_HOME>\lib目录或-Xbootclasspath参数指定的路径中的类库加载到内存中。

扩展类加载器(Extension ClassLoader):负责加载<JAVA_HOME>\lib\ext目录或java.ext.dirs系统变量指定的路径中的所有类库。

应用程序类加载器(Application ClassLoader): 负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。

五、双亲委派机制是什么,有什么好处,怎么打破

双亲委派机制:如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。
在这里插入图片描述

双亲委派模型的好处:
●主要是为了安全性,避免用户自己编写的类动态替换Java的一 些核心类,比如String。
●同时也避免了类的重复加载,因为JVM中区分不同类,不仅仅是根据类名,相同的class文件被不同的ClassLoader加载就是不同的两个类。

打破双亲委派机制则不仅要继承ClassLoader类,还要重写loadClass和findClass方法。

六、说说你JVM调优的几种主要的JVM参数

1)堆栈配置相关

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k 
-XX:MaxPermSize=16m -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxTenuringThreshold=0

-Xmx3550m: 最大堆大小为3550m。

-Xms3550m: 设置初始堆大小为3550m。

-Xmn2g: 设置年轻代大小为2g。

-Xss128k: 设置线程堆栈大小为128k。

-XX:MaxPermSize: 设置持久代大小为16m

-XX:NewRatio=4: 设置年轻代(包括Eden和两个Survivor区)与老年代的比值(除去持久代)。

-XX:SurvivorRatio=4: 设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6

-XX:MaxTenuringThreshold=0: 设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入老年代。

2)垃圾收集器相关

-XX:+UseParallelGC
-XX:ParallelGCThreads=20
-XX:+UseConcMarkSweepGC 
-XX:CMSFullGCsBeforeCompaction=5
-XX:+UseCMSCompactAtFullCollection:

-XX:+UseParallelGC: 选择垃圾收集器为并行收集器。

-XX:ParallelGCThreads=20: 配置并行收集器的线程数

-XX:+UseConcMarkSweepGC: 设置老年代为并发收集。

-XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。
-XX:+UseCMSCompactAtFullCollection: 打开对老年代的压缩。可能会影响性能,但是可以消除碎片

3)辅助信息相关

-XX:+PrintGC
-XX:+PrintGCDetails

-XX:+PrintGC 输出形式:

[GC 118250K->113543K(130112K), 0.0094143 secs] [Full GC 121376K->10414K(130112K), 0.0650971 secs]

-XX:+PrintGCDetails 输出形式:

[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs] [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs

七、JVM调优

1.将堆的最大、最小设置为相同的值,目的是防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间。
-Xmx3550m: 最大堆大小为3550m。
-Xms3550m: 设置初始堆大小为3550m。

2.在配置较好的机器上(比如多核、大内存),可以为老年代选择并行收集算法: -XX:+UseParallelOldGC 。

3.年轻代和老年代将根据默认的比例(1:2)分配堆内存, 可以通过调整二者之间的比率来调整二者之间的大小,也可以针对回收代。

比如年轻代,通过 -XX:newSize -XX:MaxNewSize来设置其绝对大小。同样,为了防止年轻代的堆收缩,我们通常会把-XX:newSize -XX:MaxNewSize设置为同样大小。

4.年轻代和老年代设置多大才算合理

1)更大的年轻代必然导致更小的老年代,大的年轻代会延长普通GC的周期,但会增加每次GC的时间;小的老年代会导致更频繁的Full GC

2)更小的年轻代必然导致更大老年代,小的年轻代会导致普通GC很频繁,但每次的GC时间会更短;大的老年代会减少Full GC的频率

如何选择应该依赖应用程序对象生命周期的分布情况: 如果应用存在大量的临时对象,应该选择更大的年轻代;如果存在相对较多的持久对象,老年代应该适当增大。但很多应用都没有这样明显的特性。

在抉择时应该根 据以下两点:
(1)本着Full GC尽量少的原则,让老年代尽量缓存常用对象,JVM的默认比例1:2也是这个道理 。
(2)通过观察应用一段时间,看其他在峰值时老年代会占多少内存,在不影响Full GC的前提下,根据实际情况加大年轻代,比如可以把比例控制在1:1。但应该给老年代至少预留1/3的增长空间。

八、类加载的机制及过程

加载>>验证>>准备>>解析>>初始化

(1)加载:在硬盘上查找并通过I0读入字节码文件,在堆内存中生成一个对象,作为方法区数据的访问入口
(2)验证: 校验字节码文件的正确性
(3)准备:给类的静态变量分配内存,并赋予默认值
(4)解析(动态链接): 将静态方法的符号引用替换为直接引用(有对应的内存地址信息)
(5)初始化: 给类的静态变量初始化为指定的值,执行静态代码块

九、Jdk1.7到Jdk1.8 java虚拟机发⽣了什么变化?

1.7中存在永久代,1.8中没有永久代,替换它的是元空间,元空间所占的内存不是在虚拟机内部,⽽是本地 内存空间,这么做的原因是,不管是永久代还是元空间,他们都是⽅法区的具体实现,之所以元空间所占 的内存改成本地内存,官⽅的说法是为了和JRockit统⼀,不过额外还有⼀些原因,⽐如⽅法区所存储的类 信息通常是⽐较难确定的,所以对于⽅法区的⼤⼩是⽐较难指定的,太⼩了容易出现⽅法区溢出,太⼤了 ⼜会占⽤了太多虚拟机的内存空间,⽽转移到本地内存后则不会影响虚拟机所占⽤的内存。

十、你们项目如何排查JVM问题 ?

分两种情况
①对于还在正常运⾏的系统:

  1. 可以使⽤jmap来查看JVM中各个区域的使⽤情况;
  2. 可以通过jstack来查看线程的运⾏情况,⽐如哪些线程阻塞、是否出现了死锁;
  3. 可以通过jstat命令来查看垃圾回收的情况,特别是fullgc,如果发现fullgc⽐较频繁,那么就得进⾏调优了 ;
  4. 通过各个命令的结果,或者jvisualvm等⼯具来进⾏分析 ;
  5. ⾸先,初步猜测频繁发送fullgc的原因,如果频繁发⽣fullgc但是⼜⼀直没有出现内存溢出,那么表示 fullgc实际上是回收了很多对象了,所以这些对象最好能在younggc过程中就直接回收掉,避免这些对 象进⼊到⽼年代,对于这种情况,就要考虑这些存活时间不⻓的对象是不是⽐较大,导致年轻代放不 下,直接进⼊到了⽼年代,尝试加⼤年轻代的⼤⼩,如果改完之后,fullgc减少,则证明修改有效;
  6. 同时,还可以找到占⽤CPU最多的线程,定位到具体的⽅法,优化这个⽅法的执⾏,看是否能避免某些 对象的创建,从⽽节省内存 。

② 对于已经发⽣了OOM的系统:
1.⼀般⽣产系统中都会设置当系统发⽣了OOM时,⽣成当时的dump⽂件(- XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/base)
2. 我们可以利⽤jsisualvm等⼯具来分析dump⽂件
3. 根据dump⽂件找到异常的实例对象,和异常的线程(占⽤CPU⾼),定位到具体的代码
4. 然后再进⾏详细的分析和调试

十一、深拷贝和浅拷贝

深拷贝和浅拷贝就是指对象的拷贝,一个对象中存在两种类型的属性,一种是基本數据类型, 一种是实例对象的引用。

1.浅拷贝是指,只会拷贝基本数据类型的值,以及实例对象的引用地址,并不会复制一份引用地址所指向的对象, 也就是浅拷贝出来的对象,内部的类属性指向的是同一个对象

2深拷贝是指,既会拷贝基本数据类型的值,也会针对实例对象的引用地址所指向的对象进行复制,深拷贝出来的对象,内部的类执行指向的不是同一个对象

十二、说⼀下JVM中,哪些可以作为GC root

首先,GC root是根对象,JVM在进⾏垃圾回收时,需要找到“垃圾”对象,也就是没有被引⽤的对象,但是直接找“垃圾”对象是⽐较耗时的,所以反过来,先找“⾮垃圾”对象,也就是正常对象,那么就需要从某些“根”开始去找,根据这些“根”的引⽤路径找到正常对象,⽽这些“根”有⼀个特征,就是它只会引⽤其他对象,⽽不会被其他对象引⽤,例如:栈中的本地变量、⽅法区中的静态变量、本地⽅法栈中的变量、正在运⾏的线程等可以作为GC root。

十三、JVM诊断工具有哪些?

阿里的Arthas
JDK自带JVM诊断工具:JAVA VisualVM

Full GC把整个堆都回收
OOM:内存溢出 Out of Memory Error

十四、为什么要使用STW?

STW:stop the word JAVA进行GC时会停止用户线程,对用户体验很不好

反证法,如果没有STW,用户线程将一直执行。
GC要进行垃圾回收,就要找所有垃圾和非垃圾,先找GC root,然后找引用对象。因为程序会在GC的过程中一直在执行,然后结束了,结束之后内存空间都被释放了,栈帧弹出,GC root已经没了,之前找的非垃圾已经全变成垃圾了。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_46460843/article/details/119482598

智能推荐

Android 实现禁用中文键盘_简德的博客-程序员宅基地

不知道该如何一句话表达,标题姑且用产品的需求描述吧。其实产品想表达的意思是,在密码输入框实现全键盘样式英文键盘。然后我们遇到一个大坑, toggleButton.setOnCheckedChangeListener(new ToggleButton.OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

启发函数 (Heuristic Function) —Octile_jubobolv369的博客-程序员宅基地_启发函数

启发函数 (Heuristic Function)盲目搜索会浪费很多时间和空间, 所以我们在路径搜索时, 会首先选择最有希望的节点, 这种搜索称之为"启发式搜索 (Heuristic Search)"如何来界定"最有希望"? 我们需要通过启发函数 (Heuristic Function)计算得到.对于网格地图来说, 如果只能四方向(上下左右)移动,曼哈顿距离(Manhattan distance)是最合适的启发函数.function Manhattan(node) = d...

谷粒商城业务逻辑(一)_lovewangyihui的博客-程序员宅基地_商城项目的业务逻辑

谷粒商城项目业务逻辑基础篇后台管理用人人开源三件套后台管理vue只配置总的请求路由,从网关调用,其它服务只需要注册到网关就可以,renren逆行生成的vue中给label标签赋值,在数据表格中要配置自己的数据样式对应表格,以及部分crud要自内容管理哪里赋予权限修改vue代码界面的返回code状态码所有模块注册到注册中心http://localhost:88/api/study/student/list?t=1634975888504&amp;page=1&amp;limit=10&

python列表有引号_你如何在python中找到带引号的数字列表的平均值?_回忆宝箱的博客-程序员宅基地

我有两种类型的列表.第一个没有引号,工作和打印平均罚款:l = [15,18,20]print lprint "Average: ", sum(l)/len(l)这打印:[15,18,20]Average: 17第二个列表包含带引号的数字,返回错误:x = ["15","18","20"]print xprint "Average: ", sum(x)/len(x)错误是:TypeError: u...

Opencv初探_yangyang_z的博客-程序员宅基地

一、第一个程序:显示图片OpenCV提供了一些使用工具来读取从视频流到摄像机画面的各种各样的图像格式,这些工具都是HighGUI的一部分。我们将使用其中的一些工具来创建一个简单程序,二、使用步骤1.引入库2.读入数据总结...

使用牛顿迭代法实现求y=(x-2)^3的解_Guapifang的博客-程序员宅基地

关于牛顿迭代法:牛顿迭代法又称为牛顿-拉夫逊(拉弗森)方法,它是牛顿在17世纪提出的一种在实数域和复数域上近似求解方程的方法。针对大部分函数,都有如下特点如上图,我们随机选取一点x0,得到函数f(x)上的一点y0,通过点(x0,y0)得到函数f(x)关于该点的切线f0,切线f0是一条直线又会在x轴上得到一点x1,同样通过x1在f(x)上得到y1,然后又在点(x1,y1)得到f(x)关于该点的切线f1,又重复下去得到x2,会发现每次得到的xi渐渐趋近我们函数和x轴的交点,这个就是利用切线不断趋近方程根

随便推点

-bash: yum: command not found_水煮胡萝卜的博客-程序员宅基地

-bash: yum: command not found 错误在学习Linux时,误把yum命令给删除了,在百度了好几篇文章,但没能解决……............查看有无yum安装包 ,有的话卸载:查看:rpm -qa |grep yum卸载:rpm -aq|grep yum|xargs rpm -e --nodeps第一步:到这个网站去下载如下RPM包 :htt...

VC++ Win32界面编程中的窗口风格要点总结(附源码)_dvlinker的博客-程序员宅基地_win32界面编程

最近我们遇到了几个与窗口风格相关的问题,因为未设置指定的窗口风格或者错误设置了某些窗口风格导致了窗口出现了一些bug。本文借此机会将窗口风格的相关要点进行详细的总结,在此分享出来,给大家提供一些借鉴和参考。

UpdateData、Invalidate、InvalidateRect和UpdateWindow及RedrawWindow_sylvia:-)的博客-程序员宅基地

原文地址:UpdateData、Invalidate、InvalidateRect和UpdateWindow及RedrawWindow作者: 蒋佩钊(jk-3126)Invalidate和UpdateWindow的区别按引:Invalidate在消息队列中加入一条WM_PAINT消息,其无效区为整个客户区。而UpdateWindow直接发送一个WM_PAINT消息,其无效区范围

oracle11g sp 1503,Oracle 12.2报错ORA-15032、ORA-15410或ORA-15411解决_从一小姐的博客-程序员宅基地

**现象:**在Oracle 12.2.0.1 RAC环境,在其ASM实例中,如果添加不同大小或者不同数量的LUN到failgroup中,会报错:ORA-15032: not all alterations performedORA-15410: Disks in disk group OCRVT do not have equal size.或者ORA-15032: not all altera...

linux centos8系统安装gitlab_约翰李的博客-程序员宅基地_centos8安装gitlab

linux系统安装gitlab1.安装相关依赖yum -y install policycoreutils openssh-server openssh-clients postfix2.启动ssh服务&amp;设置为开机启动systemctl enable sshd &amp;&amp; sudo systemctl start sshd3. 设置postfix开机自启,并启动,postfix支持gitlab发信功能systemctl enable postfix &amp;&amp; s

Java多线程编程之Two-phase Termination模式_贾诩是也的博客-程序员宅基地

停止线程是一个目标简单而实现却不那么简单的任务。首先,Java没有提供直接的API用于停止线程。此外,停止线程时还有一些额外的细节需要考虑,如待停止的线程处于阻塞(等待锁)或者等待状态(等待其它线程)、尚有未处理完的任务等。本文介绍的Two-phase Termination模式提供了一种通用的用于优雅地停止线程的方法。Two-phase Termination模式简介Java并没有提供直