ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

JVM面试题

2022-07-09 19:34:51  阅读:136  来源: 互联网

标签:面试题 Java 线程 内存 JVM GC 加载


1、java中会出现内存泄漏吗?前简述

 

会。自己实现堆载的数据结构时有可能会出现的内存泄露,可参考看effective java

 

2、64位JVM中,int的长度是多数?

 

java中,int类型变量的长度是一个固定值,与平台无关,都是32位,意思是说,在32位和64位的java虚拟机中,int类型的长度是相同的。

 

3、Serial与Parallel GC之间的不同之处?

 

Serial 与 Parallel 在 GC 执行的时候都会引起 stop-the-world。它们之间主要不同 serial 收集器是默认的复制收集器,执行 GC 的时候只有
一个线程,而parallel 收集器使用多个 GC 线程来执行。

 

4、32 位和 64 位的 JVM,int 类型变量的长度是多数?


32 位和 64 位的 JVM 中,int 类型变量的长度是相同的,都是 32 位或者 4个字节

 

5、Java 中 WeakReference 与 SoftReference 的区别?


虽然 WeakReference 与 SoftReference 都有利于提高 GC 和 内存的效率,但是 WeakReference ,一旦失去最后一个强引用,就会被 GC
回收,而软引用虽然不能阻止被回收,但是可以延迟到 JVM 内存不足的时候

 

6、JVM 选项 -XX:+UseCompressedOops 有什么作用?为什么要使用


当你将你的应用从 32 位的 JVM 迁移到 64 位的 JVM 时,由于对象的指针从32 位增加到了 64 位,因此堆内存会突然增加,差不多要翻倍。
这也会对 CPU缓存(容量比内存小很多)的数据产生不利的影响。因为,迁移到 64 位的 JVM主要动机在于可以指定最大堆大小,通过压缩
OOP 可以节省一定的内存。通过-XX:+UseCompressedOops 选项,JVM 会使用 32 位的 OOP,而不是 64 位的 OOP。

 

7、怎样通过 Java 程序来判断 JVM 是 32 位 还是 64位?


你可以检查某些系统属性如 sun.arch.data.model 或 os.arch 来获取该信息。

 

8、32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?


理论上说上 32 位的 JVM 堆内存可以到达 2^32,即 4GB,但实际上会比这个小很多。不同操作系统之间不同,如 Windows 系统大约 1.5
GB,Solaris 大约3GB。64 位 JVM 允许指定最大的堆内存,理论上可以达到 2^64,这是一个非常大的数字,实际上你可以指定堆内存大小
到 100GB。甚至有的 JVM,如 Azul,堆内存到 1000G 都是可能的。

 

9、JRE、JDK、JVM 及 JIT 之间有什么不同?


JRE 代表 Java 运行时(Java run-time),是运行 Java 引用所必须的。JDK 代表 Java 开发工具(Java development kit),是 Java 程序的开
发工具,如 Java编译器,它也包含 JRE。JVM 代表 Java 虚拟机(Java virtual machine),它的责任是运行 Java 应用。JIT 代表即时编译
(Just In Time compilation),当代码执行的次数超过一定的阈值时,会将 Java 字节码转换为本地代码,如,主要的热点代码会被准换为
本地代码,这样有利大幅度提高 Java 应用的性能。

 

10、解释 Java 堆空间及 GC?
当通过 Java 命令启动 Java 进程的时候,会为它分配内存。内存的一部分用于创建堆空间,当程序中创建对象的时候,就从对空间中分配内
存。GC 是 JVM 内部的一个进程,回收无效对象的内存用于将来的分配

 

 

11、JVM内存区域

 

 

 

 

JVM 内存区域主要分为线程私有区域【程序计数器、虚拟机栈、本地方法区】、线程共享区域【JAVA 堆、方法区】、直接内存。
线程私有数据区域生命周期与线程相同, 依赖用户线程的启动/结束 而 创建/销毁(在 Hotspot VM 内, 每个线程都与操作系统的本地线程直接
映射, 因此这部分内存区域的存/否跟随本地线程的生/死对应)。 
线程共享区域随虚拟机的启动/关闭而创建/销毁。
直接内存并不是 JVM 运行时数据区的一部分, 但也会被频繁的使用: 在 JDK 1.4 引入的 NIO 提供了基于 Channel 与 Buffer 的 IO 方式, 它可
以使用 Native 函数库直接分配堆外内存, 然后使用DirectByteBuffer 对象作为这块内存的引用进行操作(详见: Java I/O 扩展), 这样就避免了
在 Java堆和 Native 堆中来回复制数据, 因此在一些场景中可以显著提高性能。

 

 

 

12、程序计数器(线程私有)

一块较小的内存空间, 是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器,这类内存也称为“线程私有” 的内
存。
正在执行 java 方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址)
。如果还是 Native 方法,则为空。
这个内存区域是唯一一个在虚拟机中没有规定任何 OutOfMemoryError 情况的区域。

 

13、虚拟机栈(线程私有)

是描述java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、
方法出口等信息。 每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
栈帧( Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接(Dynamic Linking)、 方法返回值和异常分派(
Dispatch Exception)。 栈帧随着方法调用而创建,随着方法结束而销毁——无论方法是正常完成还是异常完成(抛出了在方法内未被捕获
的异常)都算作方法结束。

 

 

 

 

 14、本地方法区(线程私有)

 

本地方法区和java Stack 作用

本地方法区和 Java Stack 作用类似, 区别是虚拟机栈为执行 Java 方法服务, 而本地方法栈则为Native 方法服务, 如果一个 VM 实现使用 Clinkage 模型来支持 Native 调用, 那么该栈将会是一个C 栈,但 HotSpot VM 直接就把本地方法栈和虚拟机栈合二为一 。

 

15、你能保证 GC 执行吗?


不能,虽然你可以调用 System.gc() 或者 Runtime.gc(),但是没有办法保证 GC的执行

 

16、怎么获取 Java 程序使用的内存?堆使用的百分比?


可以通过 java.lang.Runtime 类中与内存相关方法来获取剩余的内存,总内存及最大堆内存。通过这些方法你也可以获取到堆使用的百分比
及堆内存的剩余空间。Runtime.freeMemory() 方法返回剩余空间的字节数,Runtime.totalMemory()方法总内存的字节数,
Runtime.maxMemory() 返回最大内存的字节数

 

 

17、java中堆和栈的区别

 

JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。栈通常都比堆小,也不会
在多个线程之间共享,而堆被整个 JVM 的所有线程共享。

 

18、描述一下JVM加载class文件的原理机制

 

JVM 中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java 中的类加载器是一个重要的 Java 运行时系统组件,它负责在运行
时查找和装入类文件中的类。

 

由于 Java 的跨平台性,经过编译的 Java 源程序并不是一个可执行程序,而是一个或多个类文件。当 Java 程序需要使用某个类时,JVM 会确
保这个类已经被加载、连接(验证、准备和解析)和初始化。类的加载是指把类的.class 文件中的数据读入到内存中,通常是创建一个字节
数组读入.class 文件,然后产生与所加载类对应的 Class 对象

加载完成后,Class 对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内
存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后 JVM 对
类进行初始化,包括:1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;2)如果类中存在初始化语句,就依次执
行这些初始化语句。

 

类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定
义类加载器(java.lang.ClassLoader 的子类)。

 

从 Java 2(JDK 1.2)开始,类加载过程采取了父亲委托机制(PDM)。PDM 更好的保证了 Java 平台的安全性,在该机制中,JVM 自带的
Bootstrap 是根加载器,其他的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类
加载器自行加载。JVM 不会向 Java 程序提供对 Bootstrap 的引用。下面是关于几个类
加载器的说明: 
1. Bootstrap:一般用本地代码实现,负责加载 JVM 基础核心类库(rt.jar);
2. Extension:从 java.ext.dirs 系统属性所指定的目录中加载类库,它的父加载器是 Bootstrap;
3. System:又叫应用类加载器,其父类是 Extension。它是应用最广泛的类加载器。它从环境变量 classpath 或者系统属性
java.class.path 所指定的目录中记载类,是用户自定义加载器的默认父加载器。

 

19、GC 是什么?为什么要有 GC?


GC 是垃 圾收 集的 意思 ,内存 处理 是编 程人 员容 易出 现问 题的 地方 ,忘记 或者 错误的内 存回 收会 导致 程序 或系 统的 不稳 定甚 至
崩 溃, Java 提供 的 GC 功能 可以 自动监测 对象 是否 超过 作用 域从 而达 到自 动回 收内 存的 目的 ,Java 语言 没有 提供 释放已分 配内
存的 显示 操作 方法 。Java 程序 员不 用担 心内 存管 理,
因为 垃圾 收集 器会自动 进行 管理 。要 请求 垃圾 收集 ,可 以调 用下 面的 方
法 之一 :System.gc() 或Runtime.getRuntime().gc() ,但 JVM 可以 屏蔽 掉显 示的 垃圾 回收 调用 。 
垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低优先级的线程运行,不可预知的情况
下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回
收。在 Java 诞生初期,垃圾回收是 Java最大的亮点之一,因为服务器端的编程需要有效的防止内存泄露问题,然而时过境迁,如今 Java 的
垃圾回收机制已经成为被诟病的东。移动智能终端用户通常觉得 iOS 的系统比 Android 系统有更好的用户体验,其中一个深层次的原因就
在于 Android 系统中垃圾回收的不可预知性

 

20、堆(Heap-线程共享) -运行时数据区


是被线程共享的一块内存区域,
创建的对象和数组都保存在 Java 堆内存中,也是垃圾收集器进行垃圾收集的最重要的内存区域。 由于现代
VM 采用分代收集算法, 因此 Java 堆从 GC 的角度还可以细分为: 新生代(Eden 区、 From Survivor 区和 To Survivor 区)和老年代。

 

21、方法区/永久代(线程共享)


即我们常说的永久代(Permanent Generation), 用于存储被 JVM 加载的类信息、 常量、 静态变量、 即时编译器编译后的代码等数据.
HotSpot VM把GC分代收集扩展至方法区, 即使用Java堆的永久代来实现方法区, 这样 HotSpot 的垃圾收集器就可以像管理 Java 堆一样管理
这部分内存,而不必为方法区开发专门的内存管理器(永久带的内存回收的主要目标是针对常量池的回收和类型的卸载, 因此收益一般很小) 。

 

运行时常量池(Runtime Constant Pool)是方法区的一部分。 Class 文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一
项信息是常量池 (Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行
时常量池中。 Java 虚拟机对 Class 文件的每一部分(自然也包括常量池)的格式都有严格的规定,每一个字节用于存储哪种数据都必须符
合规范上的要求,这样才会被虚拟机认可、装载和执行。

 

22、JVM 运行时内存


Java 堆从 GC 的角度还可以细分为: 新生代(Eden 区、 From Survivor 区和 To Survivor 区)和老年代。 

 

 

 

 

23、新生代

是用来存放新生的对象。一般占据堆的 1/3 空间。由于频繁创建对象,所以新生代会频繁触发MinorGC 进行垃圾回收。新生代又分为 Eden
区、 ServivorFrom、 ServivorTo 三个区。

MinorGC 的过程(复制->清空->互换)
MinorGC 采用复制算法。
1: eden、 servicorFrom 复制到 ServicorTo,年龄+1
首先,把 Eden 和 ServivorFrom 区域中存活的对象复制到 ServicorTo 区域(如果有对象的年龄以及达到了老年的标准,则赋值到老年代
区),同时把这些对象的年龄+1(如果 ServicorTo 不够位置了就放到老年区);
2:
清空 eden、 servicorFrom
然后,清空 Eden 和 ServicorFrom 中的对象;
3: ServicorTo 和 ServicorFrom 互换
最后, ServicorTo 和 ServicorFrom 互换,原 ServicorTo 成为下一次 GC 时的 ServicorFrom区。

 

24、老年代

主要存放应用程序中生命周期长的内存对象。
老年代的对象比较稳定,所以 MajorGC 不会频繁执行。在进行 MajorGC 前一般都先进行了一次 MinorGC,使得有新生代的对象晋身入老
年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次 MajorGC 进行垃圾回收腾出
空间。
MajorGC 采用标记清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。 ajorGC 的耗时比较长,因为要扫
描再回收。 MajorGC 会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。当老年代也满了装不
下的时候,就会抛出 OOM(Out of Memory)异常。

 

25、永久代


指内存的永久保存区域,主要存放 Class 和 Meta(元数据)的信息,Class 在被加载的时候被放入永久区域,
它和和存放实例的区域不同,GC
不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的 Class 的增多而胀满,最终抛出 OOM 异常。

 

26、JAVA8 与元数据
在 Java8 中,
永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。元空间的本质和永久代类似,元空间与永久代之间最大
的区别在于:
元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。 类的元数据放入
nativememory, 字符串池和类的静态变量放入 java 堆中,
这样可以加载多少类的元数据就不再由MaxPermSize 控制, 而由系统的实际可用
空间来控制。

 

27、引用计数法


在 Java 中,引用和对象是有关联的。如果要操作对象则必须用引用进行。因此,很显然一个简单的办法是通过引用计数来判断一个对象是
否可以回收。简单说,即一个对象如果没有任何与之关联的引用,
即他们的引用计数都不为 0,
则说明对象不太可能再被用到,那么这个
对象就是可回收对象。

 

28、可达性分析法

 

为了解决引用计数法的循环引用问题, Java 使用了可达性分析的方法。通过一系列的“GC roots”对象作为起点搜索。如果在“GC roots”和一
个对象之间没有可达路径,则称该对象是不可达的。要注意的是,不可达对象不等价于可回收对象,
不可达对象变为可回收对象至少要经
过两次标记过程。两次标记后仍然是可回收对象,则将面临回收

 

标签:面试题,Java,线程,内存,JVM,GC,加载
来源: https://www.cnblogs.com/15078480385zyc/p/16461699.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有