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.线程调优

问题:什么时候会出现内存泄漏?

  • 在堆、栈、方法区都会出现

问题:什么是强引用,软引用?


posted @ 2023-08-04 17:00  hongyc77  阅读(46)  评论(0)    收藏  举报