JVM面试高频问题

news2024/11/22 16:50:42

一、进程与线程

在谈JVM的这些问题前,我们先来复习一下有关线程和进程的关系

  • 进程可以看作是程序的执行过程。一个程序的运行需要CPU时间、内存空间、文件以及I/O等资源。操作系统就是以进程为单位来分配这些资源的,所以说进程是分配资源的基本单位。
  • 线程从属于进程,只能在进程的内部活动,多个线程共享进程所拥有的的资源。如果把进程看作是完成许多功能的任务的集合,那么线程就是集合中的一个任务元素,负责具体的功能。虽然CPU、内存、I/O等资源分配给了进程,但实际上真正利用这些资源并在CPU上执行的却是线程,即真正完成程序功能的是线程。

因为进程作为这些资源的拥有者,它的负载很重,在进程的创建、切换、删除过程中的时间和空间开销都很大。所以目前主流的操作系统都只将进程作为资源的拥有者,而把CPU调度和运行的属性赋予了线程。

比如打开浏览器程序,会产生相应的进程,浏览器进程中包含有许多线程,如HTTP请求线程,I/O线程,渲染线程,事件响应线程等。浏览器进程拥有着内存和I/O资源等,但当我们在浏览器中输入文字时,真正使用I/O资源接收我们输入的文字,并在CPU处理文字的却是浏览器进程中的I/O线程。即真正完成浏览器文字输入功能的是线程。

 现代很多操作系统支持让一个进程包含多个线程,从而提高程序的并行程度和资源的利用率。

 二、JVM进程

我们知道Java语言是需要运行在JVM上的。实际上,JVM也是一个软件程序,这就意味着它执行起来也会在操作系统中创建进程,即JVM进程,通常又叫JVM实例。而我们所写的main方法,实际上就是JVM进程中主线程的所在。

从操作系统的角度来看,我们常说的Java程序,应该包括JVM和我们编写的Java代码。

当我们写完Java代码,并编译成class文件后,使用Java命令执行main方法;或者直接在IDE启动main方法时,JVM程序就会执行,操作系统会将其从磁盘中装入内存,并创建一个JVM进程,随后启动主线程,主线程会去调用某个类的 main 方法,因此这个主线程就是我们写的main方法所在。

实际上,JVM本身就是一个多线程应用,即使我们在代码中并没有手动的创建线程,JVM进程也并不是只有一个主线程,而是也会有其他线程。这些线程完成着JVM的功能,如GC线程负责回收JVM使用过程中的垃圾对象。JVM进程启动完成后,必然会有的线程如下:

线程作用
main主线程,执行我们指定的启动类的main方法
Reference Handler处理引用的线程
Finalizer调用对象的finalize方法的线程
Signal Dispatcher分发处理发送给JVM信号的线程
Attach Listener负责接收外部的命令的线程

至此,我们知道了,启动一个Java程序,本质上就是启动JVM程序,并在操作系统中创建一个JVM进程。这个JVM进程会由操作系统分配许多资源,如内存、I/O等。JVM进程中包含有许多线程,这些线程共享JVM进程分配到的资源,同时这些线程也是CPU核心上执行的实体,它们完成着JVM所具有的功能。

同时我们通过实验也能发现,启动多少个java程序,就会创建多少个JVM进程(JVM实例)。每个实例都是独立的,互不影响,即一个程序(一个JVM软件程序)可以被多个进程共用(创建多个JVM进程或者说JVM实例)

三、JVM

JVM内存区域划分

总的来说,JVM大致可以分为四部分:

  • 方法区
  • 程序计数器

大家对java中的变量应该都不陌生吧!变量分为全局变量、局部变量、静态变量

tip: 大家要主要我们这里说的是JVM中的栈和堆,和操作系统的栈和堆、数据结构中的栈和堆是不同的。大家以后再被问到栈和堆的时候,一定要搞清楚是哪里面的栈和堆

举个例子

public class test {
    public int a; // 全局变量(也可叫做test这个类的成员变量)
    public static int b; // 静态变量
    public void method() {
        int c = 0; // 局部变量
        System.out.println("这是test这个类的一个普通成员方法");
    }
    public static void run() {
        System.out.println("这是一个静态方法!"); // 静态方法不需要借助实例化对象
    }
    public static void main(String[] args) {
        test test1 = new test(); // test1 是test类的一个实例化对象
        test test2 = new test(); // test2 是另一个实例化对象
    }
}
  1.  我们的局部变量就是存储在栈上的,全局变量属于我们new出来的实例化对象的一部分,储存在堆上,静态变量就比较特殊了,他存储在方法区中。
  2. 那么我们栈上还有什么东西呢?还有我们类中各个方法间的调用关系。

在回到堆中,我们到我们new出来的实例化对象是存储在堆上的,我们的实例化出来的每个对象都有他对应的变量和方法(即我们的成员方法和成员变量),他们自然和对象一起都储存在堆上。

那静态变量和静态方法呢?

他们不属于我们实例化出来的对象,他们是在我们我们这个类创建出来的时候就存在了,不依托实例化对象。他们属于类对象,我们方法区里放到就是类对象。

那么类对象里有啥呢?

包括类是啥名字

继承自谁,实现了那些接口

有啥属性,属性名是啥,类型是啥,访问权限是啥

有什么方法,方法名是?参数是?返回值?访问权限?方法内部的指令是?方法里面干了什么?

总之类对象对我们这个类做了一个整体的描述,一个类可以有多个实例化对象,但只有一个类对象(即每个静态变量和静态方法在不同的实例化对象中也只有一个)。

我们总结一下

  • 栈(方法之间的调用关系、局部变量)
  • 堆(实例化出来的对象:全局变量(也叫成员变量)、成员方法)
  • 方法区(类对象:静态变量、静态方法....)

哦,对了我们还有程序计数器没说

 就像我们上面说的,当我们启动一个java,启动main方法的时候,我们JVM程序就会执行,就会创建出来一个JVM进程,同时还会有多个线程来负责完成JVM的工作。那么我想问,是每个线程都有上面这四个区域吗?

不是的,我们的方法区 和 堆是整个JAVA进程中只有一份的(该进程内的多个线程共享这一份资源),但程序计数器和栈,则是每个线程都有一份。

为啥呢:

操作系统cpu等相关资源分配给进程,该进程内的所有线程共享该进程的所有资源,线程是执行的基本单位。

每个线程在执行各自执行各自的代码,各自是一个执行流,所有说每个线程都需要知道接下来要执行的指令是什么(程序计数器的功能)?每个线程也需要记录下当前的调用栈(存在方法区)

JVM类加载

Java程序启动的时候,就需要让JVM把class文件给读进内存并进行一系列后续的工作。

类加载流程

1、加载  找到class文件,打开文件,读文件,创建空的类对象

2、链接

  • 验证 —— 检查.class文件格式是否符合规范要求(JVM规范中明确描述了)
  • 准备 —— 给静态变量分配内存空间,空间里填充0值。
  • 解析 —— 把字符串常量进行初始化,把“符合引用替换成直接引用”

 

3、初始化

针对类的静态成员进行初始化,执行静态代码块,如果这个类的父类还没加载,也要去加载父类

而面试中,面试官最爱考的就是——“双亲委派模型”(描述了是类加载中的加载阶段,去那些目录里找.class文件

为了理解所谓的双亲委派模型,我们需要知道:类加载器——JVM中特殊的模块,功能就是负责把类给加载起来,完成类加载的工作。

 


 JVM垃圾回收(GC)

大家还记得吗?在C语言中,我们通过malloc动态申请内存(申请的内存是在堆中),每次申请完后都要我们手动释放内存(free)。如果不释放就回造成内存泄漏等严重问题。

但是如果光指望我们程序员手动释放内存,那显然是不靠谱的。

为在Java中就由机器负责回收不再使用的内存空间——这种机制就被称为内存回收机制(garbage collection简称GC)

 1、垃圾回收中,回收的是什么?

 

 


 2、如何确定该对象是需要回收的?

 那么我们知道了回收的单位是对象,那么我们如何具体确定某个对象就是垃圾(不再使用了呢)呢?

确定是不是垃圾,有很多种办法。其中Java里主要使用的是“可达行分析”这种办法,在别的编程语言中(比如Python)中使用的是“引用计数这种方法”。

在《深入理解Java虚拟机》这本书中,这两种办法都有提到。

  • 那么如果我们在面试中被问到:在垃圾回收机制中,如何判断对象是不是垃圾?你可以两个都说。
  • ​​​​如果问的是:在JVM中,如何判断对象是不是垃圾,你只需回答“可达性分析”就行。

 引用计数法

使用额外的计数器,来记录对于某个对象来说,有多少引用指向他。

要想使用对象,就需要有引用指向他,如果没用引用了——引用计数为0了,说明该对象无法被使用了,也就是需要回收的垃圾了。

 

可达性分析(Java真正采取的方法)

可达性,什么意思呢?

就是以代码中的一些特殊变量为起点,然后以起点触发,看看那些对象都能被访问到。只要对象能访问到,就标记为“可达”,当完成一圈标记后,剩下的就是“不可达“的了,也就是要回收的垃圾了!!!

我们对上面的一些名词做一些解释

一、什么样的变量可以可以称为起点(GCRoot)呢?

1、局部变量表中的对对象的引用(栈里面的局部变量,栈有多个,每个线程一个,每个栈里面又有很多的栈帧,每个栈帧里有一个自己的局部变量表。)

意思就是:所有线程的所有栈的所有栈帧的所有的局部变量表中的全部的变量,都可视为GCRoot.

2、常量池中对应的对象

3、方法区中,静态引用类型的成员

二、什么叫做”能够被访问到“?

 


3、确定要回收的对象后,如何具体进行垃圾回收?

有以下几个笔记经典的垃圾回收算法(策略)

1、标记-清除

 

2、复制算法(为了解决内存碎片化问题)

 

3、标记整理(类似于顺序表删除元素)

 4、分代回收

 

总结:

  1. 在伊甸区,新生的对象如果活过了一轮GC(一轮GC以及把大部分对象给回收了,活过一轮GC的是少数),就把这些幸存的对象放到幸存区(因为对象少,所以幸存区的空间不大)
  2. 幸存区的对象使用复制算法,因为数量较少,即使浪费了一半的内存也没关系。
  3. 老年代的对象使用标记整理算法,因为老年代对象他被回收的频率不高,就可以接收标记整理带来的开销

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2542.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言函数章--该如何学习函数?阿斗看了都说会学习了

前言 👻作者:龟龟不断向前 👻简介:宁愿做一只不停跑的慢乌龟,也不想当一只三分钟热度的兔子。 👻专栏:C初阶知识点 👻工具分享: 刷题: 牛客网 leetcode笔记软…

【Python入门指北】 发邮件与正则表达式

文章目录邮件发送一、群发邮件二、指定用户发邮件正则表达式一、预备知识正则1. 正则介绍2. 陷阱3. 特殊的字符二、 re 模块的方法1 常用方法2. 正则分组总结邮件发送 #第三方模块 yagmail #pip3 install yagmailimport yagmail""" 项目需求 yag yagmail.SMTP(u…

MyBatis Plus实现动态字段排序

利用周末时间,对已有的项目进行了升级,原来使用的是tkmybatis,改为mybatis plus。但是由于修改了返回数据的格式,前端页面字段排序失效了,需要刷新表格才会排序。页面效果如下 easyui的数据表格datagrid支持多字段排序…

【仿牛客网笔记】Spring Boot实践,开发社区登录模块-账号设置,检查登录

首先访问账号设置的页面。 新建一个Controller,用过RequestMapping生成访问路径 上传头像 首先打开配置文件,配置一下将文件配置到哪里。 直接在Controller存了, 更新的时候掉Map,参数为id和路径。 注入日志对象后,通过Val…

SpringBoot项目启动执行任务的几种方式

经过整理后得到以下几种常用方式,供大家参考。 1. 使用过滤器 init() :该方法在tomcat容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。可以在这个方法中补充想要执行的内容。 Component public class MyFilter …

CTF竞赛网络安全大赛(网鼎杯 )Web|sql注入java反序列化

CTF竞赛网络安全大赛题目考点 sql注入 java反序列化 网鼎杯解题思路 题目一打开是这样的界面 下载题目的附件,并用jd-gui.exe打开 核心代码如下 Test代码 `` package 部分class;import cn.abc.common.bean.ResponseCode; import cn.abc.common.bean.ResponseResult; impor…

持续交付中流水线构建完成后就大功告成了吗?别忘了质量保障

上期文章我结合自己的实践经验,介绍了持续交付中流水线模式的软件构建,以及在构建过程中的3个关键问题。我们可以看出,流水线的软件构建过程相对精简、独立,只做编译 和打包两个动作。 但需要明确的是,在持续交付过程…

网课查题接口使用方法

网课查题接口使用方法 本平台优点: 多题库查题、独立后台、响应速度快、全网平台可查、功能最全! 1.想要给自己的公众号获得查题接口,只需要两步! 2.题库: 查题校园题库:查题校园题库后台(点…

Hadoop面试题汇总-20221031

Hadoop面试题汇总 HDFS部分 1、请描述HDFS的写流程。 答: 首先由客户端向 NameNode 发起文件上传请求,NameNode 检查文件要上传的目录,并鉴权。如果上传用户对此目录有权限,则允许客户端进行上传操作。客户端接收到允许指令后&…

本科毕业论文内容必须有国内外文献综述吗?

不知不觉间整个暑假变过去了,现在大部分的大学生都已经开学了。2023届毕业的学生现在也开始借鉴毕业论文的选题工作。但是无论是现在正在选题的大四的同学们还是还在上大一大,二大三的同学们都对毕业论文这4个字有着天生的恐惧感。因为对于大多数人来说&…

阿里为何禁止在对象中使用基本数据类型

大家好,我是一航! 前两天,因为一个接口的参数问题,和一位前端工程师产生了一些分歧,需求很简单: 根据一个数值类型(type 取值范围1,2,3)来查询数据&#xff…

HTML+CSS+JavaScript七夕情人节表白网页【樱花雨3D相册】超好看

这是程序员表白系列中的100款网站表白之一,旨在让任何人都能使用并创建自己的表白网站给心爱的人看。 此波共有100个表白网站,可以任意修改和使用,很多人会希望向心爱的男孩女孩告白,生性腼腆的人即使那个TA站在眼前都不敢向前表白…

pandas 基本数据

目录 1. pandas 简介 2. pandas 基本数据结构 2.1 Series 类型 2.1.1 索引-数据的行标签 2.1.2 值 2.1.3 切片 2.1.4 索引赋值 2.2 DataFrame 类型 1. pandas 简介 一般导入的形式:import pandas as pd 2. pandas 基本数据结构 python 的数据结构&#xff1a…

python爬虫之Scrapy框架,基本介绍使用以及用框架下载图片案例

一、Scrapy框架简介 Scrapy是:由Python语言开发的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据,只需要实现少量的代码,就能够快速的抓取。 Scrapy使用了Twisted异步网络框架来处理网络通信&#xf…

Servlet篇 —— 我的第一个Servlet程序

☕导航小助手☕ 🍚写在前面 🍜一、Maven的介绍 🍱​二、第一个Servlet的创建 🍔🍔2.1 创建项目 🥩🥩​2.2 引入依赖 🦪🦪​2.3 创建目录 🍣🍣2.4…

没想到GoFrame的gcache天然支持缓存淘汰策略

gcache提供统一的缓存管理模块,提供了开发者可自定义灵活接入的缓存适配接口,并默认提供了高速内存缓存适配实现。 先说结论 这篇文章通过结合商业项目的使用场景,为大家介绍了gcache的基本使用、缓存控制以及淘汰策略。 使用gcache做缓存处…

3分钟,快速上手Postman接口测试

Postman是一个用于调试HTTP请求的工具,它提供了友好的界面帮助分析、构造HTTP请求,并分析响应数据。实际工作中,开发和测试基本上都有使用Postman来进行接口调试工作。有一些其他流程的工具,也是模仿的Postman的风格进行接口测试工…

推荐 4 个开源工具

Hi,艾瑞巴蒂,晚上好!今天推荐 4 个登上 GitHub 热搜的开源项目,它们分别是:1. 炫酷的 UI 工具:glslViewer2. Textual3. ToolJet:开源的低代码开发框架4. Linux 命令大全搜索工具01炫酷的 UI 工…

程序人生:去了字节跳动,才知道年薪40W的测试有这么多?

今年大环境不好,内卷的厉害,薪资待遇好的工作机会更是难得。最近脉脉职言区有一条讨论火了: 哪家互联网公司薪资最‘厉害’? 下面的评论多为字节跳动,还炸出了很多年薪40W的测试工程师 我只想问一句,现在的…

vue3项目的创建,vite+vue3+ts(3)- router

vue3 有三种写法: 1.compostion API : 还是按照vue2.0写法 2.组合式API: 3. 组合式API 语法糖(setup), 语法简洁(推荐使用这个) 写法: 4. 在.eslintrc.cjs 或者 .eslintrc.js中配置代码,是这个…