Java面试——JVM篇

在这里插入图片描述

JDK、JRE 和JVM的区别

JDK(Java Development Kit,java开发工具)包括Java语言、工具类 (包括编译器(javac.exe)、开发工具(javadoc.exe、jar.exe、keytool.exe、jconsole.exe))) 还有JRE(Java Runtime Environment)。开发Java程序(包括编译和运行),需要安装JDK。

JRE(Java运行环境)包含了部署技术、JavaSE API(基本的类库如java.lang和util等jar)和JVM。如果只是部署和运行Java程序,那么 实际上只安装JRE 就能达到效果。在服务器上只安装JRE的情况: 1.发布到服务器上时所有文件都是编译好的文件,包括 JSP 文件。 2. 后期不在服务器上直接修改(因为导致修改后的文件未重新编译)。而JRE中没有Java编译和翻译所需工具类(java和Javac)。

JVM(Java Virtual Machine, Java 虚拟机),用于解释字节码文件并翻译成不同操作系统的机器码。是Java实现跨平台运行的基础。
JAVA中就虚拟机是用的是C语言+汇编语言开发,除此之外其余都是java语言开发的。

如下图:
在这里插入图片描述

说一下JVM由哪些部分组成

在这里插入图片描述

JVM包含两个子系统和两个子组件:两个子系统即类加载子系统(Class Loader System)和执行引擎(Execution Engine)。两个组件为运行数据区域(Runtime Data Area)和本地接口(Native Interface )

  • 类加载器(Class Loader): 根据给定的全限定类名(如java.util.Scanner)来装载class 文件到运行时数据区域(Runtime Data Area) 的方法区(Method Area)。
  • 执行引擎(Execution Engine): 执行字节码文件中的指令
  • 本地接口(Native Interface): 与Native Libraries 交互,是与其他编程语言交互的接口。
  • 运行时数据区域(Runtime Data Area): JVM内存

执行流程:

源文件(.java)经编译器(Javac .exe)编译成字节码文件(.class )。字节码文件经类装载子系统的类加载器(Class Loader)加载(从硬盘读取到内存的过程)到运行时数据区的方法区(Method Area )。而字节码文件只是JVM的一套指令集规范,并不能直接交给底层操作 系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine)将字节码翻译成底层系统指令,再交由CPU去执行,而这个过程中需要调用其他语言的本地库接口来实现整个程序的功能。

说一下 JVM运行时数据区域

在这里插入图片描述

Java运行时数据区域包括:

  • 虚拟机栈(VM Stack): JVM为每个执行的Java方法分配一个栈帧。栈帧保存该方法的出口、局部变量表、参数、操作数栈、动态链接等。栈帧是虚拟机栈的基本单位。
  • 本地方法栈(Native Method Stack): 与虚拟机栈作用一样。只不过本地方法栈维护的是本地方法,这类方法是由c++/c语言编写。
  • 程序计数器(Program Counter Register): 当前线程所执行的字节码的行号指示器,字节码解析器的工作是通过改变这个计数器的数值,来选取下一条需要执行的字节码指令。分支、循环、跳转、异常处理都是基于这个计数器来实现的
  • Java 堆(Java Heap): Java虚拟机中内存最大的一块,是被所有线程共享,几乎所有的对象和数组实例都在这里分配内存。
  • 方法区(Method Area): 用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

详细介绍程序计数器

程序计数器是一块较小的内存空间,可以看成当前线程所执行字节码的行号指示器。字节码解释器工作时通过改变程序计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转异常处理、线程恢复都依赖于这个计数器。
为了使得线程切换后还能恢复到正确的执行位置,每个线程都有自己的一个计数器,各个线程之间的计数器互不影响,这类内存区域被称为“线程私有”的内存
这个程序计数器记录的是正在执行的虚拟机字节码指令的地址,但是如果是native方法,则程序计数器的值为空(undefined)。
此内存区域是唯一一个在Java虚拟规范中没有规定任何OutOfMemoryError情况的区域。

详细介绍Java 虚拟机栈

与程序计数器一样,该空间也是线程私有的,其生命周期与线程相同。
虚拟机栈描述的是Java方法执行时的内存模型:每个方法执行时都会创建一个栈帧,该栈帧中存储方法的**局部变量表、操作数栈、动态链接、方法的出口等信息。**每个方法从调用到执行结束就对应着一个栈帧在虚拟机栈的入栈和出栈。
如果线程请求的栈深度大于虚拟允许的深度(递归调用方法太多时),将抛出 StakOverflowError异常。
如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

什么是栈帧

每个方法执行时都会创建一个栈帧,该栈帧中存储方法的**局部变量表、操作数栈、方法的出口、动态链接等等信息。

在这里插入图片描述

在这里插入图片描述

什么是局部变量表? 操作数栈?

局部变量表:局部变量表存放的是一个方法内部存储的局部变量和方法参数,该变量可以是基本类型(boolean、byte、char、short、int、float)也可以是引用类型(reference)和returnAddress类型 (returnAddress 中保存的是return后要执行的字节码的指令地址)。
操作数栈:和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组。但是和前者不同的是,它不是通过索引来访问,而是通过标准的栈操作—压栈和出栈—来访问的。比如,如果某个指令把一个值压入到操作数栈中,稍后另一个指令就可以弹出这个值来使用。

通过一个实例来了解局部变量和操作数栈:
来源

实例方法中的局部变量表

public class T {

    private int a = 0;

    public void add(int b,int c){
        a = b + c;
    }
}

原来,JVM在编译代码的时候,偷偷在局部变量表中添加了一个this引用(很明显this保存的实例的引用),这也是我们为什么可以在方法中访问实例中的成员变量的原因,如下

在这里插入图片描述

操作栈:

图5

图中字节码的简要解释如下:
0)aload_0 将this的引用入栈 (aload_0即将局部变量表中索引为0的引用压到操作数栈中)
1)iload_1 将参数b入栈 (将局部变量中的索引为1的整数压到操作数栈中)
2)iload_2 将参数c入栈

此时栈的内容有(0为栈顶)
0.c
1.b
2.this

3)iadd 将栈顶的两个数相加,并将结果保存至栈顶,此时栈的内容为
0.b+c
1.this

4). putfield 将栈顶的两个值出栈,第一个值(b+c)赋值给第二个值(this)的对应的成员变量(是的,没错即使是赋值也要执行两次出栈操作)

动态链接: 在Java源文件被编译成字节码文件时所有方法引用都作为符号引用(Symbolic Reference)保存在方法区的常量池中。而在 实际调用过程中,会将符号引用转换为直接引用(该引用的实际内存地址),这一过程就是动态链接。
例如:Java源代码中Person per = new Person();符号引用为全类名:com.johnny.Person ,而在调用时会动态查找该符号引用对应的内存地址。
在这里插入图片描述
方法出口: 出口就是return,不正常的话就抛出异常。

垃圾回收算法

Java中会存在内存泄漏吗?

Java中可能存在内存泄漏情况。当长生命周期的对象持有端生命周期对象的引用时,就很可能发生内存泄漏。

Person p1 = new Person(“zs”,15);

public class Test01 {
    static Vector v = new Vector(10);
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            Object obj = new Object();
            v.add(obj);
            obj = null;
        }
    }

p1是引用变量, 右边new Person(“zs”, 15)是对象。
一个对象可以拥有多个引用,一个引用只能同时绑定0个或1个对象。

Minor GC、 Major GC、 Full GC 是什么?

  1. MinorGC 是新生代GC,指的是发生在新生代的垃圾收集动作。由于 Java对象大都是朝生夕死的,所以MinorGC 非常频繁,一般回收速度也比较快。 (一般采用复制算法)
  2. Major GC 是老年代GC,指的是发生在老年代的垃圾收集动作。通常执行Major GC会连着Minor GC 一起执行。Major GC 的速度要比Minor GC 慢很多。(可采用标记-清除算法 和 标记-整理算法)
  3. Full GC 是清理整个堆空间,包括新生代和老年代。

Minor GC 、Major GC 的触发条件

Minor GC的触发条件一般为:

  1. Eden区满时,触发Minor GC 。
  2. 当新建的对象大小 大于 Eden所剩空间时触发Minor GC

Major GC 的触发条件 一般为:

  1. 晋升到老年代的对象大小超过了老年代剩余空间
  2. 堆内存分配了很大的对象

Full GC 的触发条件一般为:

  1. 执行Syste.gc()
  2. CMS GC 异常

为什么新生代要分Eden 和两个Survivor区域?

  1. **降低Major Gc 触发的次数。**如果没有Survivor ,Eden区每进行一次Minor GC ,存活对象就会被送到老年代。老年代很快就被填满,从而触发Major GC。而老年代的内存空间远大于新生代的内存空间,进行一次Major GC 消耗的时间远大于MinorGC。所以需要分Eden和Survivor区。
  2. 除此之外,Survivor 存在的意义,就是减少送往老年代 的对象,进而减少Major GC 的发生。Survivor的预筛选,保证了只有经历了15次Minnor GC 还能在新生代中存活的对象才会被送往老年代。
  3. 避免了空间碎片化。新建的对象在Eden区,经过一次Minnor GC,Eden区存活的对象就会被复制到Survivor Space(即S0)区域,Eden区被清空。等Eden区满了,就再触发一次MinorGC。这时Eden区存活的对象就会和s0区的对象复制到第二块Survivor Space(即S1)中,然后清空Eden和S0区。这种复制算法保证了S1来自S0和Eden两部分的存活对象占用连续的内存空间。

为什么要分代(新生代和老生代)?

主要原因就是可以根据各个年代的特点,对对象分区存储,更便于采用最合适的拉圾回收算法回收。
在新生代中: 每次垃圾回收都有大批对象死去,只有少数对象存活。因此只需要付出时候少量的存活对象复制成本就可以完成收集。所以采用复制算法。

而老年代因为对象存活率高,又没有额外的空间给予担保,就必须采用“标记- 清除”或者“标记-整理”算法。

对象存放过程:

  1. 数据首先会分配到Eden区(大对象则直接放入老年代)。当Eden没有足够的空间的时候就会触发JVM发起一次Minor GC。
  2. 如果对象经过一次Minor GC还存活且又能被Survivor 空间接受,那么将被移动到Survivor空间中,并将年龄设置为1。对象在Survivor每熬过一次Minor GC ,年龄就加1 。 当年龄达到一定程度(默认为15)时,就会被晋升到 老年代中。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Java全能学习面试手册——Java面试题库.zip 01 7道消息队列ActiveMQ面试题!.pdf 02 10道Java高级必备的Netty面试题!.pdf 03 10道Java面试必备的设计模式面试题!.pdf 04 10个Java经典的List面试题!.pdf 05 10个Java经典的Main方法面试题!.pdf 06 10个Java经典的String面试题!.pdf 07 15道经典的Tomcat面试题!.pdf 08 15道面试常问的Java多线程面试题!.pdf 09 17道消息队列Kafka面试题!.pdf 10 18道非常牛逼的Nginx面试题!.pdf 11 20道顶尖的Spring Boot面试题!.pdf 12 20道面试官常问的JVM面试题!.pdf 13 22道面试常问的SpringMVC面试题!.pdf 14 24道经典的英语面试题!.pdf 15 24道消息队列RabbitMQ面试题!.pdf 16 27道顶尖的Java多线程、锁、内存模型面试题!.pdf 17 29道常见的Spring面试题!.pdf 18 30个Java经典的集合面试题!.pdf 19 36道面试常问的MyBatis面试题!.pdf 20 40道常问的Java多线程面试题!.pdf 21 55道BAT精选的Mysql面试题!.pdf 22 60道必备的Java核心技术面试题!.pdf 23 70道阿里巴巴高级Java面试题!.pdf 24 ActiveMQ消息中间件面试专题.pdf 25 Dubbo服务框架面试题及答案.pdf 26 Dubbo面试.pdf 27 Dubbo面试题.pdf 28 Dubbo面试专题.pdf 29 Java 面试题经典 77 问!.pdf 30 Java架构课程MCA大纲.pdf 31 Java就业班体系结构大纲.pdf 32 java面试题_多线程(68题).pdf 33 java面试题_设计模式(26题).pdf 34 java面试题_微服务--dubbo(41题).pdf 35 java面试题_消息中间件--RabbitMQ(20题).pdf 36 java面试题_消息中间件--RocketMq(14题).pdf 37 JVM.pdf 38 JVM常见面试题指南.pdf 39 JVM面试题.pdf 40 JVM面试专题.pdf 41 JVM性能优化相关问题.pdf 42 JVM执行子系统.pdf 43 Kafka面试题.pdf 44 Linux面试题.pdf 45 Linux面试专题.pdf 46 MongoDB面试题.pdf 47 MongoDB面试专题.pdf 48 Mybatis面试题(含答案)_.pdf 49 MyBatis面试题.pdf 50 MyBatis面试专题.pdf 51 MySQL55题答案.pdf 52 MySQL面试题(含答案)_.pdf 53 mysql面试题.pdf 54 mysql面试专题.pdf 55 MySQL性能优化的21个最佳实践.pdf 56 Netty面试题.pdf 57 Netty面试专题.pdf 58 Nginx面试题.pdf 59 Nginx面试专题.pdf 60 Nginx实战书籍.pdf 61 RabbitMQ消息中间件面试专题.pdf 62 Redis面试题(二).pdf 63 Redis面试题(含答案)_.pdf 64 Redis面试题.pdf 65 Redis面试专题(二).pdf 66 Redis面试专题.pdf 67 Redis实战.pdf 68 SpringBoot面试专题.pdf 69 SpringCloud面试专题.pdf 70 SpringMVC面试专题.pdf 71 Spring面试题(含答案)_.pdf 72 Spring面试专题及答案.pdf 73 Spring源码深度解析.pdf 74 Tomcat架构解析文档.pdf 75 Tomcat架构解析文档.pdf.baiduyun.downloading 76 Tomcat面试专题.pdf 77 Tomcat优化相关问题.pdf 78 Zookeeper+分布式过程.pdf 79 zookeeper面试专题.pdf 80 并发编程面试专题.pdf 81 并发面试题.pdf 82 多线程,高并发.pdf 83 多线程面试59题(含答案)_.pdf 84 分布式缓存 Redis + Memcached 经典面试题!.pdf 85 搞定 HR 面试的 40 个必备问题!.pdf 86 集合框架.pdf 87 精选7道Elastic Search面试题!.pdf 88 精选8道Dubbo面试题!.pdf 89 精选17道海量数量处理面试题!.pdf 90 看透Spring

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值