JVM面试题整理

请你谈谈jvm的理解?

在这里插入图片描述

java->class->jvm

jvm调优都是在调方法区(1%)和堆(99%)

第三方插件一般是在执行引擎上改动

在这里插入图片描述

jvm的位置
jvm的体系结构
jvm的类加载器

包括加载、链接、初始化
在这里插入图片描述

实例的引用(名字)放在java栈,对象放在堆里面

1.虚拟机自带的加载器

2.启动类(根)加载器

3.扩展类加载器

4.应用程序加载器
在这里插入图片描述

双亲委派机制

保证安全

原理:app->exc->boot(最终执行)

扩展类加载器和根加载器都没有的情况下,就会去找应用程序加载器

沙箱安全机制
native
pc寄存器
程序计数器
方法区

method area:一种特殊的堆

方法区存放在永久代区。

方法区:线程共享的。

java栈

与程序计数器一样,Java 虚拟机栈也是线程私有的,它的生命周期和线程相同,描述的是 Java 方法执行的内存模型,每次方法调用的数据都是通过栈传递的。

Java 内存可以粗糙的区分为堆内存(Heap)和栈内存 (Stack),其中栈就是现在说的虚拟机栈,或者说是虚拟机栈中局部变量表部分。 (实际上,Java 虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。)

局部变量表主要存放了编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。

Java 虚拟机栈会出现两种错误:StackOverFlowErrorOutOfMemoryError

  • StackOverFlowError 若 Java 虚拟机栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误。

  • OutOfMemoryError Java 虚拟机栈的内存大小可以动态扩展, 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

  • Java 虚拟机栈也是线程私有的,每个线程都有各自的 Java 虚拟机栈,而且随着线程的创建而创建,随着线程的死亡而死亡。

    扩展:那么方法/函数如何调用?

    Java 栈可用类比数据结构中栈,Java 栈中保存的主要内容是栈帧,每一次函数调用都会有一个对应的栈帧被压入 Java 栈,每一个函数调用结束后,都会有一个栈帧被弹出。

    Java 方法有两种返回方式:

    1. return 语句。
    2. 抛出异常。

    不管哪种返回方式都会导致栈帧被弹出。

java栈 stack

本地方法栈 native method stack

栈帧:在这里插入图片描述

方法索引(index)

输入输出参数()

本地变量

CLASS file:引用

父帧:往栈底方向链接下一个

子帧:往栈顶出口链接上一个

2.3 本地方法栈

和虚拟机栈所发挥的作用非常相似,区别是: 虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。

本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。

方法执行完毕后相应的栈帧也会出栈并释放内存空间,也会出现 StackOverFlowErrorOutOfMemoryError 两种错误。

三种jvm

sun公司 hotpot

bea jrockit

ibm

我们学习都是hotspot

堆 heap

一个jvm只有一个堆,堆内存的大小是可以调节的。

类加载器读取了类文件后,把 类、方法、常量、变量、保存所有引用类型的真实对象

Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。

Java 世界中“几乎”所有的对象都在堆中分配,但是,随着 JIT 编译期的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了。从 JDK 1.7 开始已经默认开启逃逸分析,如果某些方法中的对象引用没有被返回或者未被外面使用(也就是未逃逸出去),那么对象可以直接在栈上分配内存。

Java 堆是垃圾收集器管理的主要区域,因此也被称作GC 堆(Garbage Collected Heap).从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代:再细致一点有:Eden 空间、From Survivor、To Survivor 空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。

在 JDK 7 版本及 JDK 7 版本之前,堆内存被通常被分为下面三部分:

  1. 新生代内存(Young Generation)

    1.1伊甸园区

    1.2幸存区0区 from

    1.3幸存区1区 to(默认15次交换后就会晋升到老年区)

  2. 老生代(Old Generation)

  3. 永生代(Permanent Generation)8以后称元空间

真理:经过研究,99%的对象都是临时对象

默认情况下,分配的总内存是电脑内存的1/4,而初始化的内存是1/64.

新生区
  • 类:诞生和 成长的地方,甚至死亡
  • 伊甸园,所有的对象都在此new出来
  • 幸存区(0区,1区)
老年区
永久区(持久代)1.8以后叫元空间

这个区域常驻内存的。用来存放jdk自身携带的Class对象。interface元数据,存储的是java运行时的一些环境或者类信息,该区域不存在垃圾回收,关闭vm就会释放这个区域的内存。

如果:一个启动类,加载了大量的第三方jar包,Tomcat部署了太多的应用,大量动态生成的反射类。不断的被加载,直到oom。

  • jdk1.6之前 :永久代,常量池是在方法区;

  • jdk1.7:永久代,慢慢退化,去永久代,常量池在堆中

  • 在JDK8以后,,永久存储区变成元空间,无永久代,常量池在元空间

堆内存调优
GC

gc垃圾回收,主要是在伊甸园区和养老区

假设内存满了,OOM,内存不够outofmemory

手动只能提醒jvm回收,还是靠jvm自动回收

jvm在进行GC时,大部分时候,回收都是新生代~

  • 新生代

  • 幸存区(form,to )一次交换加一岁,当一个对象经历了15次GC,都还没有死,就会进入老年区

  • 老年代

jvm晋升到老年代的动态年龄判断算法?

虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代
新的动态年龄判断算法:Survivor区的对象年龄从小到大进行累加,当累加到 X 年龄时的总和大于50%(可以使用-XX:TargetSurvivorRatio=? 来设置保留多少空闲空间,默认值是50),那么比X大的都会晋升到老年代。

轻GC和重GC分别在什么时候发生?

GC两种类:轻GC(普通的GC针对新生代,偶尔幸存区),重GC(全局GC所有的都释放掉)

GC常用算法

标记清除法,标记压缩,复制算法,引用计数法,怎么用的?

引用计数法:

对象用一次 加一次,如果没用过,就会被清除。

引用计数法和可达性分析是判断是否可以回收,剩下的是回收算法。

复制算法:

主要用在新生代(伊甸园、幸存区to、幸存区from)

好处:没有内存的碎片~

坏处:浪费了内存空间,多了一半空间永远是空to。

复制算法最佳使用场景:对象存活度较低的时候;新生区~

标记清除法

扫描这些对象:对活着对象进行一次标记。

清除:对没有标记的对象,进行清除

优点:不需要额外空间~

缺点:两次扫描,严重浪费时间,会产生内存碎片。

标记压缩法:

优点:对标记清楚法的优化,防止内存碎片,再次扫描,向一端移动存活的对象、

缺点:多了一次移动成本。

gc算法总结:

内存效率(时间复杂度):复制算法》标记清除》标记压缩

内存整齐度:复制算法=标记压缩》标记清除

内存利用率:标记压缩算法=标记清除算法》复制算法

没有最优算法,只有最合适算法:分代收集算法:

年轻代:存活率低,复制算法

老年代:存活率高,标记清除+标记压缩混合实现

JMM:java memory model

1.java内存模型

2.作用:缓存一致性协议,用于定义数据读写的规则

jmm定义了线程工作内存和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory)

多个线程修改同一个变量时,解决共享对象可见性问题:加volatile关键字,修改立即刷新进主内存

jmm 8种指令。

对八种操作规则和对volatile的一些特殊规则就能确定哪里操作是线程安全,哪些操作是线程不安全的了。

java8虚拟机和之前的变化?

什么是oom?项目中出现oom,如何排除?

-Xms1024mb(设置初始化内存分配大小)1/64

-Xmx8096mb (设置最大分配内存),默认1/4

-XX:+PrintGCDetails //打印GC垃圾回收

-XX:+HeapDumpOnOutOfMemoryError //oom DUMP 然后用mat、jprofiler工具查看分析。

  • 能够看到代码第几行出错:内存快照分析工具,MAT(Memory Analyzer Tool),jprofiler
  • debug,项目代码不多时可以一行行分析

插件MAT,jprofiler作用:

  • 分析dump内存文件,快速定位内存泄漏;
  • 获取堆中数据;
  • 获取内存快照

什么是栈溢出stackOverFlowError?

error和exception的区别?

内存快照如何抓取,怎么分析dump文件?

谈谈jvm中,类加载器你的认识?rt-jar ext application。

项目中遇到的jvm难题,是如何调优的?

**当初在智能问答项目中,我们的程序放在linux服务器测试环境上,运行了一段时间后,项目停止了,查看日志显示Cannot allocate memory,遇到了内存不足的情况,**这时就考虑是不是内存不够的原因了,**服务器上物理内存太小,**大部分都是因为程序太多,内存吃紧,而给jvm分配的内存太大(java程序启动需要的内存,linux不能给),在测试环境下重新调整java程序jvm内存

解决方案

1.在linux服务器上利用top命令查看所有进程,看哪些进程占用的内存太大了,选择性的kill,释放内存。

2.调整tomcat中对jvm内存的配置,因为jvm主要内存就是指jvm堆内存

cd到tomcat的/bin,修改配置文件catalina.sh

**JAVA_OPTS="-server -Xms2048m -Xms16*1024m -XX:+PrintGCDetails //打印GC垃圾回收 - XX:+HeapDumpOnOutOfMemoryError"oom DUMP 然后用mat、jprofiler工具查看分析。**能够看到代码第几行出错:内存快照分析工具,MAT(Memory Analyzer Tool),jprofiler

后面发现是初始化加载词向量的时候没有分页,导致内存报警。

插件MAT,jprofiler作用:

  • 分析dump内存文件,快速定位内存泄漏;
  • 获取堆中数据;
  • 获取内存快照

Java 堆内存开关/JVM的常用调优参数有哪些?

Java提供了大量的内存开关(参数),我们可以用它来设置内存大小和它们的比例。下面是一些常用的开关:

VM 开关VM 开关描述
-Xms设置JVM启动时堆的初始化大小。
-Xmx设置堆最大值。
-Xmn设置年轻代的空间大小,剩下的为老年代的空间大小。
-XX:PermGen设置永久代内存的初始化大小。
-XX:MaxPermGen设置永久代的最大值。
-XX:SurvivorRatio提供Eden区和survivor区的空间比例。比如,如果年轻代的大小为10m并且VM开关是-XX:SurvivorRatio=2,那么将会保留5m内存给Eden区和每个Survivor区分配2.5m内存。默认比例是8。(幸存区from和幸存区to是一样大小)
-XX:NewRatio提供老年代和年轻代的比例大小。默认值是2。

年轻代应该比老年代小。年轻代不宜过大,笼统说确实如此,因为在最坏情况下young gen里可能所有对象都还活着,而如果它们全部都要晋升到old gen的话,那old gen里的剩余空间必须能容纳下这些对象才行,这就需要old gen比young gen大(否则young GC就无法进行,而必须做full GC才能应付了
代的大小为10m并且VM开关是-XX:SurvivorRatio=2,那么将会保留5m内存给Eden区和每个Survivor区分配2.5m内存。默认比例是8。(幸存区from和幸存区to是一样大小) |
| -XX:NewRatio | 提供老年代和年轻代的比例大小。默认值是2。 |

年轻代应该比老年代小。年轻代不宜过大,笼统说确实如此,因为在最坏情况下young gen里可能所有对象都还活着,而如果它们全部都要晋升到old gen的话,那old gen里的剩余空间必须能容纳下这些对象才行,这就需要old gen比young gen大(否则young GC就无法进行,而必须做full GC才能应付了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java虚拟机(JVM)是负责运行Java应用程序的软件。下面是一些常见的Java JVM面试题及其答案: 1. 元空间会产生内存溢出吗?在什么情况下会产生内存溢出? 元空间不会产生内存溢出。元空间是JVM中用于存储类和元数据的区域,它的大小不受JVM堆内存限制。当元空间中的类和元数据数量超过了元空间的限制,会抛出OutOfMemoryError错误。 2. JVM内存模型中的哪些区域是线程私有的? JVM内存模型中,线程私有的区域包括程序计数器、虚拟机栈和本地方法栈。 3. 什么是OOM异常?它通常在什么情况下发生? OOM(Out of Memory)异常表示内存溢出。当JVM无法为应用程序分配足够的内存时,就会抛出OOM异常。常见的内存溢出情况包括堆内存不足、栈溢出和元空间溢出。 4. JVM的垃圾回收算法有哪些? JVM的垃圾回收算法包括标记-清除算法、复制算法、标记-整理算法和分代收集算法。 5. 什么是垃圾回收器?JVM中有哪些常见的垃圾回收器? 垃圾回收器是负责执行垃圾回收的组件。在JVM中,常见的垃圾回收器包括Serial、Parallel、CMS和G1等。 6. JVM调优的方法有哪些? JVM调优的方法包括调整堆内存大小、选择合适的垃圾回收器、优化代码以减少内存占用、减少对象的创建和销毁等。 7. 什么是类加载器?有哪些不同类型的类加载器类加载器负责将类字节码加载到JVM中,并在运行时动态加载类。在Java中,常见的类加载器包括启动类加载器(Bootstrap Class Loader)、扩展类加载器(Extension Class Loader)和应用程序类加载器(Application Class Loader)。 8. JVM的性能调优工具有哪些? JVM的性能调优工具包括jstat、jmap、jstack、VisualVM等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值