JVM
一、解决什么问题
1.解决Java跨平台问题的
2.解决Java如何让计算机运行的
3.解决内存的分配释放,如垃圾回收gc
4.安全性,如字节码验证,安全沙箱,异常处理
5.多线程
6.动态加载,即动态的加载和卸载代码,这样比较灵活和可扩展,如即时编译器JIT
二、如何解决的
1.跨平台?
方法:通过不同平台使用不同的JVM
问题:JVM底层是如何实现跨平台的(即不同平台哪里不一样,实质上考虑的是操作系统是在哪几个方面不一样)?
-
二进制格式:不同操作系统上的JVM使用不同的二进制格式。例如,Windows上的JVM使用PE(Portable Executable)格式,而Linux上的JVM使用ELF(Executable and Linkable Format)格式。
-
内存管理:不同操作系统上的JVM可能使用不同的内存管理机制。例如,Windows上的JVM使用虚拟内存管理,而Linux上的JVM使用分页机制。
-
线程调度:不同操作系统上的JVM可能使用不同的线程调度算法。例如,Windows上的JVM使用抢占式调度算法,而Linux上的JVM使用时间片轮转调度算法。
-
文件系统:不同操作系统上的JVM可能使用不同的文件系统接口。例如,Windows上的JVM使用Win32 API,而Linux上的JVM使用POSIX API。
-
网络通信:不同操作系统上的JVM可能使用不同的网络通信接口。例如,Windows上的JVM使用Winsock API,而Linux上的JVM使用Socket API。
2.如何让Java在计算机上运行的?
方法:通过解释器和即时编译器来让其变成二进制机器码
3.如何实现内存管理的
4.如何实现动态加载的
三、有哪些组件

1.类加载器
a.解决什么问题?
加载字节码文件,创建Class对象
问题:字节码和Class对象有什么区别?
- 字节码是一个执行的代码,Class对象是代表一个类或接口在内存中的表示
(感觉上就是字节码是一个执行步骤,具体的东西是Class对象,反射则是反射出字节码、代码,这些人们能看到的,反射其实是一个相反的操作)
(将Class对象想成也是一个模板,只不过是放在内存中,我们看不到,反射出来才能看到)
(为什么叫动态加载,就是因为在运行时,通过Class对象来创建实例以及获取其方法或者变量)
(为什么需要用到反射???,为什么需要在运行时才去加载创建对象的实例?)
问题:Class对象是什么?
- 它是具体的东西,是实实在在的,在内存中表示某个类或者接口的东西
问题:为什么要创建Class对象?
- 这样才能在内存中表示和管理类,那么创建之后把它放到内存当中,进入运行数据区的是是Class对象,Class对象是放在方法区当中
问题:这个Class对象有什么作用?
-
类的验证,即安全性
-
类的准备,预先分配类的空间,以及初始化
-
类的解析,解析的是常量池中的符号引用
-
类的使用,可以通过反射来实例化对象,调用方法等(实例化是像创造一个具体的人一样)
-
总结,其实就是字节码执行的时候才能真正的创造实例
问题:为什么要实例化对象?
- 为了创建具体的东西,比如通过一个人的模块,创建出小洪这个具体的人
b.如何解决问题的(即如何加载)?
-
首先需要找到文件进行加载,然后验证一个格式的正确性,然后是准备静态变量的内存空间,然后是解析符号引用变为直接引用,然后是初始化即静态变量的赋值
问题:符号引用是什么?就是常量池中的写的,这个是为了和真实地址映射,加载的时候才会变为直接引用,在加载之前是放在字节码文件中的常量池
c.组件: 使用双亲委派机制?有多少个类加载器?
- 四个,分别为启动类加载器,扩展类加载器,应用类加载器,自定义类加载器,它们加载时查询的位置不一样,然后使用双亲委派一个个向上委派,看看哪个能够进行加载,这样可以防止重复加载
d.自己的想法:能不能不使用双亲委派去查找,因为核心类库就那么多,直接整一个hashMap去查找会不会快点,这样就是利用空间换时间
2.执行引擎
a.解决什么问题?
用来执行字节码指令的
b.如何解决问题的?
解释执行逐条解释字节码指令执行,即时编译将字节码编译为本地机器码后执行,提高执行效率。
JTT通常执行的是热点代码,然后进行优化执行效果高。
问题:JIT如何进行动态加载的?
- 通过进行对热点数据的跟踪,然后来执行的,代码中是如何做到的?
c.组件:解释器,即时编译器JIT
3.运行时数据区
a.解决什么问题?
用来放线程私有和共享的运行时的数据(即放数据的)
b.如何解决问题的?
那放数据需要区分数据,因此有很多区域
-
方法区:用来放Class对象的,JIT编译后的代码,线程共享
-
程序计数器:记录当前正在执行的字节码指令的地址,线程私有;其次每次解释器或者JTT执行的时候会找它
-
堆:放实例和数组的,线程共享
-
Java虚拟机栈:每个线程都有一个栈,线程私有,它方法调用、返回信息、局部变量等
-
本地方法栈:用于执行本地方法的
-
运行时常量池:字面量和符号引用
-
直接内存:跟Java NIO有关
问题:什么是直接内存? -
在代码上,其中io使用的byte来分配,而NIO使用的是ByteBuffer的来分配
4.内存管理系统-垃圾回收器

Java8之后不在堆内存中了,8之前可以用垃圾回收机制,但是8之后是JVM直接释放内存是运行时动态进行的
a.解决什么问题?
解决内存的释放
b.如何解决问题的?
不同的区域的内存解决方法不一样
方法区和堆区使用的垃圾回收机制
栈使用的是JVM自动释放的功能,即方法调用完毕就释放
问题:垃圾回收机制如何实现的(思想)?
- 先找垃圾:引用计数和可达性分析
- 然后再做清除操作:标记清除,复制(新生代),标记整理(老年代)
问题:具体的垃圾回收操作组件有什么?
- serial垃圾回收器:单线程
- parallel垃圾回收器:多线程
- CMS垃圾回收器:解决的问题是传统垃圾回收器需要进行暂停运用程序,它可以在运行的时候进行回收,使用标记清除算法
- G1垃圾回收器:将堆内存分为多个独立的区域,使用并发执行垃圾回收,会暂停运行的应用,并发体现在执行多个区域的垃圾
5.安全
a.解决什么问题?
解决了字节码文件错误的问题,解决了一些异常问题,限制了Java程序访问系统敏感资源的问题
b.如何解决的?
- 字节码文件:通过在类加载器的时候进行验证
- 系统敏感资源:通过安全沙箱机制进行限制,实现是通过安全管理器
- 异常处理:JVM会根据程序进行try-catch的
问题:安全沙箱都对Java做出了哪一些限制:
- 限制了其对文件系统、网络、类加载和反射、数据库、系统属性的访问
问题:安全管理器包含哪些内容:
(默认情况没有开启)
- 安全策略文件、安全异常处理、安全策略检查、安全策略控制(通过编程的方式)
6.线程
a.解决什么问题?
解决了多线程运行的问题
b.如何解决的?
使用线程调度器(基于系统提供的机制),其算法主要是抢占式资源调用(即优先级高的先执行)
有如下几个步骤:线程创建、启动、调度、执行、阻塞、唤醒、终止
问题:start()和run()的区别?
- 使用start方法启动线程,会创建新的线程并执行run方法中的代码。
- 直接调用run方法只是普通的方法调用,run方法会在当前线程中执行。
7.通信接口
a.解决什么问题?
解决的是不同进程或线程之间的通信问题,即数据传输、协调操作
b.如何解决的?
使用共享内存(线程),管道和队列(线程),网络套接字(进程),远程方法调用(进程、主机)
问题:RPC是什么?
- RPC是远程过程调用,用于实现不同主机或进程之间的方法调用和数据传输
问题:RPC有哪些框架?
- RMI,Dubbo,spring cloud, gRPC
8.本地方法接口
a.解决什么问题?
你想调用本地方法如一些python代码的时候可以用,就像是一个预留的接口
总结:类加载器通过地址查找到字节码文件,然后创建Class对象,静态变量,放入方法区,并且将常量池放入到运行时常量池并将符号引用变为直接引用,执行引擎执行字节码文件,然后就回去对应的内存位置查找东西,并且放到对应的位置上,比如说创建实例就需要放到堆中,比如方法放到栈中,然后方法执行结束就有JVM来删除,其中使用垃圾回收机制来回收堆中和元空间的东西。
问题:执行引擎是如何查找到内存中的东西的?
它通过程序计数器可以进行记录每次的位置,而且程序计数器对应的位置上是有对应的符号引用,刚开始是逐条执行的。
四、如何使用这些组件
五、如何进行调优
1.使用性能分析工具,如 JProfiler、VisualVM可以分析内存泄漏
2.调整堆的大小
3.调整垃圾回收参数设置 -Xmx 和 -Xms 参数,防止内存溢出
4.线程调优
问题:什么时候会出现内存泄漏?
- 在堆、栈、方法区都会出现
问题:什么是强引用,软引用?

浙公网安备 33010602011771号