521. Java如何实现跨平台的?
Java编译生成的是字节码文件.class文件,而不是特定于某个操作系统的机器码。
不同操作系统上都有各自实现的JVM,负责将字节码翻译成特定平台的机器代码并执行。使得JAVA文件可以被不同操作系统上的JVM运行。包装了一层。
9807. JVM的组成部分
主要组成部分:
编译好的JAVA字节码(.class文件)准备就绪。
- 类加载器子系统:将class文件加载到内存中(运行时的数据区)。
- 运行时数据区。
- 执行引擎(命令解释器):将字节码文件翻译成机器码,并交给CPU执行;
- 本地方法接口:过程中会调用不同语言提供的接口,比如驱动和..,调用本地方法接口,例如操作系统级别的功能或者高性能库。
522. 编译执行和解释执行
编译执行:先将源代码编译为机器代码,再在CPU上运行。例如:C,C++;
啊
- 优点:编译后运行速度快,并且运行时,不需要再进行翻译。
解释执行:运行时,解释器逐行翻译并执行例如Python。
- 跨平台性好, 每个代码都是在每个平台上通过相应平台的解释器运行。
- 速度慢,每次执行都需要动态翻译。
=> Java采样编译执行和解释执行相结合的方式:
- 解释执行:JVM将.JAVA文件=>.class字节码。 有助于程序的跨平台性;
- 即时编译:将经常执行的代码编译为本地机器码,避免反复解释
523. JVM的内存区域如何划分的❗?
JVM运行时的数据区分为:1. 方法区 2. 堆 3. 虚拟机栈 4. 本地方法栈 5. 程序计数器。
-
方法区 - 存储类&共享信息
- 存储类信息、常量、静态变量
- 这些信息属于线程共享区域
-
Java堆 - 与JVM共存亡
- 存放所有线程共享的对象实例 和 数组 (垃圾回收主要战地)
-
虚拟机栈
- 每个线程创建一个栈:用来保存局部变量、操作数栈、方法出口信息。
- 局部变量:基本数据类型;以及对象引用;
- 栈与线程共存亡
-
本地方法栈
- 为本地方法服务。。。
-
程序计数器
- 保存当前线程执行的字节码指令地址或行号。
总结:Java程序与线程在JVM内存中的流程
- 程序启动:JVM初始化内存区域,加载类信息到方法区。
- 线程创建:为线程分配程序计数器、Java虚拟机栈和本地方法栈。
- 方法调用:线程执行方法时,创建栈帧并压入Java虚拟机栈。
- 对象创建:对象实例存储在Java堆中,元数据存储在方法区。
- 垃圾回收:JVM清理不再使用的对象和类信息。
- 线程结束:线程的栈和程序计数器被销毁。
- 程序结束:JVM释放所有内存区域并退出。
524. JVM中堆和栈的区别是什么?
栈:主要用于存储局部变量(基本类型+对象引用)和方法的调用信息(返回地址、参数等)。线程执行时,会创建该线程的栈帧,被压入Java虚拟机栈中。 执行结束,线程栈帧被弹出(销毁)
堆:主要用于存放对象实例和数组。
526. Java常量池
Java中的常量池是用于存储运行时的常量或符号的区域
- 运行时常量池:在每个类or接口的Class文件中存储编译生成的常量信息,并在类加载时进入JVM方法区;
- 字符串常量池:用于存储字符串字面量,通过String.intern()方法可以将字符串加入到字符串常量池。
🏷️常量池的作用:
- 主要呢,就是用于减少重复对象的创建,节省内存并提高效率。
🏷️字符串常量池:
- 直接使用字面量:
String s = "Hello";会将""Hello存储在常量池中,如果常量池已存在"Hello",则不会重复创建。 - 使用
new关键字: 使用String s = new String("Hello");不论常量池中是否已存在Hello,都会在堆中创建一个新的String对象。
🏷️常量池的存储内容
常量池不仅仅存储字符串常量,还包括:
- 基本类型的字面量 eg. 整数、浮点数;
- 类和接口的引用;
- 方法和字段的符号引用。
527. Java类加载器
动态加载类文件的组件。将.class文件的字节码加载到内存中,并将其转换为Class对象,以供JVM执行。
🏷️类加载器的作用:
- 动态加载类:在运行时根据需要加载类,而不是在编译时加载所有类;
- 隔离不同的类命名空间:通过不同的类加载器,可以隔离同名类,使得它们不会相互冲突。
🏷️扩展知识
JDK8一共有三种类加载器
- 启动类加载器(Bootstrap ClassLoader), 它属于虚拟机自身的一部分,主要负责加载 <JAVA_HOME>\lib 目录中或被 -Xbootclasspath指定的路径中被虚拟机识别的文件。 它是所有类加载器的父亲。
- 扩展类加载器(Extension ClassLoader), 它是Java实现的,独立于虚拟机,主要负责加载 <JAVA_HOME>\lib\ext 目录中的或被 java.ext.dirs 系统变量指定路径的类库。
- 应用程序类加载器(Application ClassLoader), 它是Java实现的,独立于虚拟机。 主要负责加载用户类路径(classPath)上的类库,程序默认的加载器。
532. java中的强引用、软引用、弱引用和虚引用
- 强引用(默认)
- 最常见的引用类型。普通对象的对象引用就是强引用。
- 只要有一个对象有强引用指向它,垃圾回收期永远不会回收该对象。
- 软引用(常用于缓存)
- 当内存不足时,会被垃圾回收清理
// 弱引用、虚引用。
533. Java中常见的垃圾收集器❗❗❗
=> 分为新生代收集器和老年代收集器,包括如下:
🏷️新生代垃圾收集器:
- Serial收集器
- 单线程收集器,适合小型应用和单处理环境。
- 触发 Stop-The-World操作,所有应用线程在GC是暂停。
- 适合单线程应用和客户端模式。
535. 为什么Java的垃圾收集器将堆分为老年代和新生代?
主要是为了提高垃圾回收效率,依据对象的生命周期特点来进行优化。
对象的生命周期特点:
- 大多数对象存活时间短:=> 分配到新生代
- 少部分对象存货时间长:=> 晋升为老年代
按照存活时间分区管理更加高效。
不同的回收算法:
- 新生代的回收:生命周期短 => 新生代通常采用复制算法;
- 老年代的回收:存活时间长 => 标记-整理算法 or 标记-清除算法。
分区后可以减少GC暂停的时间。