2022年11月10日牛客测试题题目整理

本文最后更新于:3 个月前

001 - 一个类具体是怎么加载的?

参考:

一种三个阶段,加载、链接、初始化。加载是将静态的字节码加载到JVM中,链接是将静态的数据和运行中的JVM信息关联起来,初始化是对静态变量和静态代码块等内容赋初始值(用户定义的初始值)。

1.加载阶段

将来自于class文件、jar文件、网络数据源等地方的字节码文件加载到JVM里面,并在JVM中映射为JVM认可的class对象

2.链接阶段

(1)验证

验证字节码是否合法、是否安全,以防破坏JVM的安全运行。

(2)准备

将类或接口中的静态变量设置初始值,此处的重点是给静态变量分配内存空间,赋予默认的初始值,而不是用户定义的显示初始值。

比如静态变量static int a = 1;,此处会初始化为 int 类型的初始值 0,而不是初始化为 1。

(3)解析

常量池中的 符号引用 替换为 直接引用。

符号引用是字面量,在JVM中对应的对象可以还未存在。直接引用是地址值,即指针、偏移量或句柄,指向的对象在内存中必须已经存在。

举例:中国的首都是字面量,北京市是直接引用。

解析主要对 类或接口,字段,类方法,接口方法,方法类型等进行解析。

3.初始化阶段

准备阶段主要对静态变量分配空间,初始化阶段主要对类的静态变量显示赋初始值和执行静态代码块的逻辑。

即静态变量赋初始值、执行静态代码块的逻辑。


知识扩展:
  1. Java 8之前的类加载器

(1)启动类加载器(Bootstrap Class-Loader),加载jre/lib下面的jar文件,如rt.jar.

(2)扩展类加载器(Extension or Ext Class-Loader),负责加载我们放到jre/lib/ext目录下面的jar包,这就是所谓的extension机制。该目录也可以通过设置“java.ext.dirs”来覆盖。

(3)应用类加载器(Application or App Class-Loader),就是加载我们最熟悉的classpath的内容。这里有一个容易混淆的概念,系统(System)类加载器,通常来说,其默认就是JDK內建的应用类加载器。

自定义类加载器:

1
java -Djava.system.class.loader=com.yourcorp.YourClassLoader HelloWorld

如果我们指定了这个参数,JDK內建的应用类加载器就会成为定制加载器的父亲,这种方式通常用在类似需要改变双亲委派模式的场景。

参考图:

image-20221110143031413

2.双亲委派机制

当类加载器(Class-Loader)试图加载某个类型的时候,除非父加载器找不到相应的类型,否则尽量将这个任务代理给当前加载器的父加载器去做。

参考上面这个结构图就很容易理解了。试想,如果不同类加载器都自己加载需要的某个类型,那么就会出现多次重复加载,完全是种浪费。

通常类加载器机制有三个基本特征:

  • 双亲委派模型。但不是所有类加载都遵守这个模型,有的时候,启动类加载器所加载的类型,是可能要加载用户代码的。比如JDK内部的ServiceProvider/ServiceLoader机制,用户可以在标准API框架上,提供自己的实现,JDK也需要提供些默认的参考实现。例如,Java中JNDI、JDBC、文件系统、Cipher等很多方面,都是利用的这种机制,这种情况就不会用双亲委派模型去加载,而是利用所谓的上下文加载器。
  • 可见性。子加载器可以访问父加载器加载的类型,但是反过来是不允许的。不然,因为缺少必要的隔离,我们就没有办法利用类加载器去实现容器的逻辑。
  • 单一性。由于父加载器的类型对于子加载器是可见的,所以父加载器中加载过的类型,就不会在子加载器中重复加载。但是注意,类加载器“邻居”间,同一类型仍然可以被加载多次,因为互相不可见。

002 - 一个JVM程序有多少个类加载器?

参考:

【前言】

  • 类加载的作用是通过类名获取二进制字节流
  • 主要分为四种类加载器:启动类->扩展类->应用类->自定义类
  • 双亲委派的好处:越基础的类交给越高级的类加载器
  • 问题:只有一个加载器来加载全部的类不行吗

JVM需要不同的类加载器,而不是使用同一个类加载器,本质上是对类有不同的需求所导致的。不同的场景下使用不同的类加载器,目的是更加的灵活。

在明确 目的的情况下, 专用代码 比 通用代码 更简单,也更有效。

使用不同的类加载器,方便在不同的路径下加载不同的类。更加的灵活,也方便对类进行管理。

举例两个场景:

(1)在JVM中运行不同的程序,每个程序依赖同一个 x 类,但是依赖的该类的版本不同。有的需要版本高的 x 类,有的需要版本低的 x 类。因此使用不同的类加载器可以加载不同的类。

(2)Java具有面向切面进行功能增强的特性。怎样实现修改一个类进行特性的功能增强,而不对其它的类库产生影响呢?一个方面的方式就是对每个类库使用独立的类加载器。

003 - mysql的性能瓶颈在哪里,怎么排查

【参考】

【大纲】

  • OS层面的检查
    • 检查总体的负载情况
    • 检查哪个进程的负载高
  • MySQL层面的检查
    • 查看查看慢查询日志,来找到效率低的SQL语句,想办法对这个语句进行优化。

1.OS层面的检查

OS层面的检查的目的是需要检查服务器上哪些进程的负载高。通常服务器上容易成为性能瓶颈的是磁盘的IO动作。

第一步:查看整体的负载。

整体负载高的话,那么每个进程都慢。可以通过执行指令w或者sar -q 1来查看负载。

执行上述命令之后,得到结果中,字段load average表示当前的CPU有多少任务在排队等待。load 数值超过 5 的话,负载就挺高了。

子问题1:引起 CPU 负载高的可能的原因?

  • 某些进程消耗更多的 CPU 资源,比如需要响应大量的请求(进程的事)
  • 物理内存不足,需要频繁的进行 swap(内存的事)
  • 磁盘的 IO 比较慢,导致 CPU 一直等待IO(磁盘的事)
  • 发生严重的中断,比如因为网络原因。(其它的事)

第二步:查看具体是哪个进程的负载高。

使用top命令可以查看每个进程的资源占用情况。

image-20221110161326648

如上图,

整体来看,通过 us 和 wa 的数值过高,可以推测当前的性能瓶颈可能是用户进行消耗 CPU 以及磁盘 IO等待 消耗CPU。

分开来看,看到下面每行中,第一、二行的进程, 字段 CPU 的数值很高,可以推出这两个进程是性能瓶颈。

查看到上面的 wa 数值高,推测是 IO 消耗大,因此这时可以执行 iotop 命令,查看哪些进程的磁盘 IO 消耗最大。

1
2
3
4
5
6
7
8
[yejr@imysql.com:~ ]# iotop
Total DISK READ: 60.38 M/s | Total DISK WRITE: 640.34 K/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
16397 be/4 mysql 8.92 M/s 0.00 B/s 0.00 % 94.77 % mysqld --basedir=/usr/local/m~og_3320/mysql.sock --port=3320
7295 be/4 mysql 10.98 M/s 0.00 B/s 0.00 % 93.59 % mysqld --basedir=/usr/local/m~og_3320/mysql.sock --port=3320
14295 be/4 mysql 10.50 M/s 0.00 B/s 0.00 % 93.57 % mysqld --basedir=/usr/local/m~og_3320/mysql.sock --port=3320
14288 be/4 mysql 14.30 M/s 0.00 B/s 0.00 % 91.86 % mysqld --basedir=/usr/local/m~og_3320/mysql.sock --port=3320
14292 be/4 mysql 14.37 M/s 0.00 B/s 0.00 % 91.23 % mysqld --basedir=/usr/local/m~og_3320/mysql.sock --port=3320

可以看到,端口号是3320的实例消耗的磁盘I/O资源比较多,那就看看这个实例里都有什么查询在跑吧。

2.MySQL层面的检查

首先看下当前都有哪些查询在运行(横版查看):mysqladmin pr|grep -v Sleep

1
2
3
4
5
6
7
8
9
10
[yejr@imysql.com(db)]> mysqladmin pr|grep -v Sleep
+----+----+----------+----+-------+-----+--------------+-----------------------------------------------------------------------------------------------+
| Id |User| Host | db |Command|Time | State | Info |
+----+----+----------+----+-------+-----+--------------+-----------------------------------------------------------------------------------------------+
| 25 | x | 10.x:8519 | db | Query | 68 | Sending data | select max(Fvideoid) from (select Fvideoid from t where Fvideoid>404612 order by Fvideoid) t1 |
| 26 | x | 10.x:8520 | db | Query | 65 | Sending data | select max(Fvideoid) from (select Fvideoid from t where Fvideoid>484915 order by Fvideoid) t1 |
| 28 | x | 10.x:8522 | db | Query | 130 | Sending data | select max(Fvideoid) from (select Fvideoid from t where Fvideoid>404641 order by Fvideoid) t1 |
| 27 | x | 10.x:8521 | db | Query | 167 | Sending data | select max(Fvideoid) from (select Fvideoid from t where Fvideoid>324157 order by Fvideoid) t1 |
| 36 | x | 10.x:8727 | db | Query | 174 | Sending data | select max(Fvideoid) from (select Fvideoid from t where Fvideoid>324346 order by Fvideoid) t1 |
+----+----+----------+----+-------+-----+--------------+-----------------------------------------------------------------------------------------------+

通过上面的结果可以看到还有不少的慢查询在进行。通过 slow query log 也能发现慢的 SQL 语句。

慢查询语句为:

1
select max(Fvideoid) from (select Fvideoid from t where Fvideoid>404612 order by Fvideoid) t1

这种方式先正排序,然后查询最大。查询效率非常低,因为只需要求最大值,却需要全表扫描。

优化方式为:查询之后倒排,然后取第一条。

1
select Fvideoid from t where Fvideoid>404612 order by Fvideoid desc limit 1;

3.小结

在实际的生产环境中,可能导致产生性能瓶颈的原因通常有:

(1)IO瓶颈:

  • 一次请求读写的数据量太大,比如一条SQL可能要读取几万行数据,这种情况最好减少一次读写的量

(2)没有合适的索引:

  • 没有建立合适的索引帮助进行过滤、排序、分组等。这个时候可以添加索引或者SQL改写

(3)并发量压垮服务器:

  • 瞬间请求量太大,导致服务器雪崩

(4)服务器自适应节能策略导致反应不及时

服务器自身的节能策略发现负载较低时会让CPU降频,当发现负载升高时再自动升频,但通常不是那么及时,结果导致CPU性能不足,抗不过突发的请求;

004 - http常见的状态码

  • http常见的状态码要背一下,不要只知道大概。比如每类记3个
  • (方法)尝试不要根据数字记状态,否则容易记混,根据状态记数字。
  • 2022年11月11日默写:
    • 100继续,101根据客户端要求升级协议
    • 200ok,201成功创建,202成功接收,204成功但无返回内容
    • 301永久重定向,302临时重定向,304未改变
    • 400语法错误,401未授权,403丑拒,404未找到
    • 500服务器内部错误,501内支持,502网关处出错,503服务器不可用
信息响应(100–199)
  • 继续 - 100
  • 服务器根据客户端的请求切换协议 - 101
成功响应(200–299)(缺3)
  • 请求成功 - 200
  • 已创建。成功请求并创建了新的资源 - 201
  • 已接受。已经接受请求,但未处理完成 - 202
  • 无内容。服务器成功处理,但未返回内容 - 204
重定向(300–399)(缺0、3)
  • 资源被永久转移到其它的URL - 301
  • 临时移动 - 302
  • 未修改 - 304
客户端错误(400–499)(缺2)
  • 请求的语法错误 - 400(我不李姐) - Bad Request
  • 授权 - 401 - Unauthorized(农行广研面试问题)
  • 禁止 - 403 - Forbidden
  • 请求的资源不存在 - 404 - not found
服务器错误 (500–599)
  • 内部服务器错误 - 500 - Internal Server Error
  • 功能实现 - 501 - Not Implemented
  • (网关处出问题)网关从服务器收到了一个无效的响应 - 502 - Bad Gateway
  • (服务器处出问题)服务不可用,比如正在维护 - 503 - Service Unavailable

005 - Java是值传递还是引用传递

值传递,值传递,值传递


2022年11月10日牛客测试题题目整理
https://alec-97.github.io/posts/2145513789/
作者
Shuai Zhao
发布于
2022年11月10日
许可协议