一、Java虚拟机原理:JVM为什么被称为机器
Java 是一种跨平台的语言,实现了“一次编写,到处运行”。而 Java 之所以有这种神奇的特性,就是因为 Java 编译的字节码文件不是直接在底层的系统平台上运行的,而是在 Java 虚拟机 JVM 上运行,JVM 屏蔽了底层系统的不同,为 Java 字节码文件构造了一个统一的运行环境。JVM 本质上也是一个应用程序,启动以后加载执行 Java 字节码文件。JVM 的全称是 Java Virtual Machine,样一个程序为什么被称为机器(Machine)呢?
1.1 JVM 组成构造
JVM 主要由类加载器、运行时数据区、执行引擎三个部分组成。
JVM封装了一组自定义的字节码指令集,有自己的程序计数器和执行引擎,像 CPU 一样,可以执行运算指令。它还像操作系统一样有自己的程序装载与运行机制,内存管理机制,线程及栈管理机制,看起来就像是一台完整的计算机,这就是 JVM 被称作 machine(机器)的原因。
1.2 JVM垃圾回收
事实上,JVM 比操作系统更进一步,它不但可以管理内存,还可以对内存进行自动垃圾回收。
二、数据库原理:为什么PrepareStatement性能更好更安全?
我们在 Java 程序中访问数据库的时候,有两种提交 SQL 语句的方式,一种是通过 Statement 直接提交 SQL;另一种是先通过 PrepareStatement 预编译 SQL,然后设置可变参数再提交执行。
2.1 数据库机构与SQL执行过程
一个 SQL 提交到数据库,经过连接器将 SQL 语句交给语法分析器,生成一个抽象语法树 AST;AST 经过语义分析与优化器,进行语义优化,使计算过程和需要获取的中间数据尽可能少,然后得到数据库执行计划;执行计划提交给具体的执行引擎进行计算,将结果通过连接器再返回给应用程序。
2.2 使用 PrepareStatement 执行 SQL 的好处
主要有两个好处:
一个是 PrepareStatement 会预先提交带占位符的 SQL 到数据库进行预处理,提前生成执行计划,当给定占位符参数,真正执行 SQL 的时候,执行引擎可以直接执行,效率更好一点。
另一个好处则更为重要,PrepareStatement 可以防止 SQL 注入攻击。因为一开始构造 PrepareStatement 的时候就已经提交了查询 SQL,并被数据库预先生成好了执行计划,后面黑客不管提交什么样的字符串,都只能交给这个执行计划去执行,不可能再生成一个新的 SQL 了,也就不会被攻击了。
三、Java Web程序的运行时环境到底是怎样的?
我们将工程源码编译打包以后得到的是一个 war 包,这个 war 包放入 Tomcat 的应用程序路径下,启动 Tomcat 就可以通过 HTTP 请求访问这个 Web 应用了。在这个场景下,进程是哪个?线程有哪些?Web 程序的 war 包是如何启动的?HTTP 请求如何被处理?Tomcat 在这里扮演的是什么角色?JVM 又扮演什么角色?
3.1 Tomcat启动
首先,我们是通过执行 Tomcat 的 Shell 脚本启动 Tomcat 的,而在 Shell 脚本里,其实启动的是 Java 虚拟机,在操作系统里面可以看到一个JVM虚拟机进程。
这个虚拟机进程启动以后,加载 class 进来执行,首先加载的就这个org.apache.catalina.startup.Bootstrap类,这个类里面有一个main()函数,是整个 Tomcat 的入口函数,JVM 虚拟机会启动一个主线程从这个入口函数开始执行。
主线程从 Bootstrap 的 main() 函数开始执行,初始化 Tomcat 的运行环境,这时候就需要创建一些线程,比如负责监听 80 端口的线程,处理客户端连接请求的线程,以及执行用户请求的线程。创建这些线程的代码是 Tomcat 代码的一部分。
3.2 Tomcat运行
初始化运行环境之后,Tomcat 就会扫描 Web 程序路径,扫描到开发的 war 包后,再加载 war 包里的类到 JVM。
如果有外部请求发送到 Tomcat,也就是外部程序通过 80 端口和 Tomcat 进行 HTTP 通信的时候,Tomcat 会根据 war 包中的 web.xml 配置,决定这个请求 URL 应该由哪个 Servlet 处理,然后 Tomcat 就会分配一个线程去处理这个请求,实际上,就是这个线程执行相应的 Servlet 代码。
Tomcat 会根据 HTTP 请求 URL 执行应用中的代码,这个时候,可以理解成每个请求分配一个线程,每个线程执行的都是我们开发的 Web 代码。如果 Web 代码中包含了创建新线程的代码,Tomcat 的线程在执行代码时,就会创建出新的线程,这些线程也会被操作系统调度执行。
3.3 异常处理
如果 Tomcat 的线程在执行代码时,代码抛出未处理的异常,那么当前线程就会结束执行,这时控制台看到的异常信息,其实就是线程堆栈信息,线程会把异常信息以及当前堆栈的方法都打印出来。事实上,这个异常最后还是会被 Tomcat 捕获,然后 Tomcat 会给客户端返回一个 500 错误。单个线程的异常不影响其他线程执行,也就是不影响其他请求的处理。
但是如果线程在执行代码的时候,抛出的是 JVM 错误,比如OutOfMemoryError,这个时候看起来是应用 crash,事实上是整个进程都无法继续执行了,也就是进程 crash 了,进程内所有应用都不会被继续执行了。
极客时间《后端技术面试38讲》学习笔记 Day08