• [持续更新]计算机经典面试题基础篇Day1


    [通用]计算机经典面试题基础篇Day1

    1、jvm的组成

    1. 类加载器(Class Loader):负责将编译后的Java类加载到JVM中,并在运行时动态加载所需的类。
    2. 运行时数据区(Runtime Data Area):是JVM的内存管理区域,主要包括方法区、堆、栈、程序计数器等。
    3. 执行引擎(Execution Engine):负责执行字节码指令,可以通过解释器、即时编译器(JIT)等方式来实现。
    4. 垃圾回收器(Garbage Collector):负责自动管理堆内存的分配和释放,回收无用对象的内存。
    5. 本地方法接口(Native Interface):允许Java代码调用本地的C、C++等语言编写的代码。
    6. JNI(Java Native Interface):允许Java代码与本地代码进行交互,调用本地的C、C++等语言编写的方法。

    2、jvm中的堆和栈的理解

    1. 方法区(Method Area):方法区是JVM的一部分,用于存储类的结构信息,如类的字段、方法、常量池、静态变量等。它是所有线程共享的区域。在JVM的规范中,并没有明确规定方法区的实现方式,不同的JVM实现可以有不同的方法区实现方式。
    2. 堆(Heap):堆是JVM中存储对象实例的区域,是Java程序中创建的对象所分配的内存区域。堆是所有线程共享的区域。在JVM启动时就会创建一个堆,堆的大小可以通过启动参数进行调整。堆被划分为年轻代和老年代,其中年轻代又分为Eden空间、Survivor空间(From和To)。
    • Eden空间:对象的初始分配都在Eden空间中进行,当Eden空间满时触发Minor GC,并将存活的对象复制到Survivor空间。
    • Survivor空间:存放从Eden空间中幸存的对象,当Survivor空间满时触发Minor GC,并将存活的对象复制到另一个Survivor空间,同时清空当前Survivor空间。
    • 老年代:存放长时间存活的对象,当老年代满时触发Full GC。
    1. 栈(Stack):栈是JVM中的一个线程私有区域,用于存储方法的局部变量、操作数栈、方法返回值和异常处理信息等。每个线程在执行方法时都会创建一个栈帧,栈帧中保存了方法的局部变量、操作数栈等信息。随着方法的调用和返回,栈帧会被入栈和出栈。
    2. 程序计数器(Program Counter):程序计数器是JVM中的一个线程私有区域,用于记录当前线程执行的字节码指令的地址。每个线程都有一个独立的程序计数器,它指向当前正在执行的指令,在线程切换时会保存和恢复当前线程的程序计数器的值。程序计数器是线程私有的,不会发生内存溢出的情况,并且不会进行垃圾回收。

    3、java类加载的三种方式

    Java类加载是指将类的字节码文件加载到JVM中,并进行类的初始化和连接的过程。Java类加载有三种方式:

    1. 隐式加载(隐式类加载):当程序在执行过程中使用new关键字创建对象或者调用静态方法、静态变量时,JVM会自动加载相应的类。这是最常见的类加载方式,也是默认的类加载方式。
    2. 显式加载(显式类加载):使用Class.forName()方法来显式加载类。Class.forName()方法会根据提供的类的全限定名(包括包名)来加载类并返回对应的Class对象。这种方式可以动态加载类,根据运行时的条件决定加载哪个类。
    3. 被动加载(被动类加载):当一个类被引用,但并没有实际使用到该类的时候,类不会被加载。只有在真正使用到类时,才会加载该类。常见的被动加载场景包括通过子类引用父类的静态字段、通过数组定义引用类、常量在编译阶段会存入调用类的常量池等。

    4、说说你对java中双亲委派机制的理解

    双亲委派机制是Java类加载机制中的一种。它的核心思想是当类加载器需要加载一个类时,它会先委托给父类加载器去尝试加载,只有当父类加载器无法加载该类时,才会由当前类加载器去加载。这样可以保证类的唯一性和安全性,避免重复加载和恶意代码的加载。双亲委派机制在Java中起到了重要的作用,实现了类的共享和重用。

    5、请你说说对gc的理解

    GC(Garbage Collection)是一种自动内存管理机制,用于自动回收不再使用的对象所占用的内存空间。它通过标记、清除和压缩等步骤来完成垃圾回收的过程。程序员无需手动触发,由JVM的垃圾回收器自动执行。合理的编码和内存管理可以最大程度发挥GC的作用,提高程序性能和稳定性。

    6、 hashmap容怎么实现扩容

    HashMap在插入元素时,会根据负载因子(load factor)来判断是否需要进行扩容操作。负载因子是指哈希表中已存储元素个数与实际容量的比值。

    当HashMap的负载因子超过设定的阈值时(默认为0.75),就会触发扩容操作。扩容会创建一个新的更大的哈希表,并将原有的元素重新分配到新的哈希表中,以减少哈希冲突,提高查询效率。

    HashMap的扩容过程大致包括以下几个步骤:

    1. 创建新的哈希表,其容量是原哈希表的两倍。新哈希表的容量一般会选择最接近且大于原容量的2的幂次方。
    2. 遍历原哈希表中的每个桶(bucket),将桶中的元素重新计算哈希值并分配到新哈希表中的对应桶中。这一步骤会重新计算元素的哈希值和索引位置,确保元素在新哈希表中的位置发生变化。
    3. 将新哈希表设置为当前哈希表,原哈希表则成为垃圾对象等待垃圾回收。

    扩容操作可能会对性能产生一定的影响,因为需要重新计算哈希值和重新分配元素。为了减少扩容的频率,可以通过调整负载因子的大小来控制HashMap的容量和性能之间的平衡。较小的负载因子会使哈希表更快地扩容,但会占用更多的内存空间,较大的负载因子则会减少扩容的次数,但可能会导致哈希冲突增多。

    7、谈谈你对hashtable和currenthashmap的理解

    Hashtable和ConcurrentHashMap都是Java中的线程安全的哈希表实现,它们在功能和使用方式上有一些相似之处,但在内部实现和性能方面有一些区别。

    1. Hashtable: Hashtable是最早引入的哈希表实现,它是线程安全的,所有的操作都是同步的(通过synchronized关键字实现)。由于同步的原因,HashTable在多线程环境下的性能比较低,只能通过同一时刻只允许一个线程访问的方式来保证线程安全。
    2. ConcurrentHashMap: ConcurrentHashMap是Java 5中引入的高性能线程安全的哈希表实现。它通过使用分段锁(Segment)来实现并发访问的高效性。每个Segment相当于一个小的HashTable,可以独立地进行操作,不同的Segment之间可以并发地进行读写操作。这样,在大多数情况下,不同的线程可以同时操作不同的Segment,提高了并发访问的效率。ConcurrentHashMap在并发环境下具有较高的性能和扩展性。
    3. 区别:
    • 线程安全性:Hashtable是通过同步的方式实现线程安全,而ConcurrentHashMap是通过分段锁(Segment)实现高效的并发访问。
    • 性能:ConcurrentHashMap相对于Hashtable在多线程环境下具有更好的性能,可以支持更高的并发度。
    • 迭代器弱一致性:Hashtable的迭代器是强一致性的,即在迭代过程中不会发生修改。而ConcurrentHashMap的迭代器是弱一致性的,可以在迭代过程中发生修改,但不会抛出ConcurrentModificationException异常。

    如果需要在多线程环境下使用哈希表且对性能要求较高,推荐使用ConcurrentHashMap。

    如果在单线程环境下或性能要求不高的情况下,可以使用HashTable。

    8、说一下tcp的三次握手和四次挥手

    TCP(Transmission Control Protocol)是一种可靠的、面向连接的网络传输协议。在建立和关闭TCP连接时,需要进行三次握手和四次挥手的过程。

    三次握手(Three-way Handshake)的过程如下:

    1. 第一次握手:客户端发送一个带有SYN(同步)标志的TCP报文段给服务器,请求建立连接。此时客户端进入SYN_SENT状态。
    2. 第二次握手:服务器接收到客户端的请求后,回复一个带有SYN和ACK(确认)标志的报文段给客户端。此时服务器进入SYN_RECEIVED状态。
    3. 第三次握手:客户端接收到服务器的回复后,再次发送一个带有ACK标志的报文段给服务器,表示连接已建立。此时连接建立完成,客户端和服务器都进入ESTABLISHED状态,可以开始进行数据传输。

    四次挥手(Four-way Handshake)的过程如下:

    1. 第一次挥手:客户端发送一个带有FIN(结束)标志的报文段给服务器,表示客户端不再发送数据。客户端进入FIN_WAIT_1状态。
    2. 第二次挥手:服务器接收到客户端的结束请求后,发送一个带有ACK标志的报文段给客户端,表示服务器收到了结束请求。此时服务器进入CLOSE_WAIT状态。
    3. 第三次挥手:服务器发送一个带有FIN标志的报文段给客户端,表示服务器不再发送数据。服务器进入LAST_ACK状态。
    4. 第四次挥手:客户端接收到服务器的结束请求后,发送一个带有ACK标志的报文段给服务器,表示客户端收到了结束请求。客户端进入TIME_WAIT状态,等待一段时间后关闭连接。服务器接收到ACK后,关闭连接,进入CLOSED状态。

    通过三次握手,客户端和服务器建立起可靠的连接;通过四次挥手,双方完成数据传输并安全地关闭连接。这样可以确保数据的可靠传输和连接的正常释放。

    9、说一下tcp和udp的区别

    TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是两种常用的传输层协议,它们在特点和适用场景上有以下几点区别。

    1. 连接性:
    • TCP是面向连接的协议,通过三次握手建立连接,提供可靠的、有序的、面向字节流的数据传输。TCP确保数据的完整性和可靠性,适用于对数据准确性要求较高的场景。
    • UDP是无连接的协议,不需要建立连接,直接发送数据包。UDP提供了一种简单的、无拥塞控制的数据传输方式,适用于对实时性要求较高,但对可靠性要求相对较低的场景。
    1. 数据传输特性:
    • TCP提供可靠的数据传输,通过序列号和确认机制,保证数据的按序到达且不丢失。TCP还提供流量控制和拥塞控制机制,以避免网络拥塞和数据丢失。
    • UDP提供不可靠的数据传输,数据包可能会丢失、重复、乱序。UDP没有流量控制和拥塞控制机制,数据传输速度更快,但不保证数据的可靠性。
    1. 数据报大小:
    • TCP没有固定的最大数据报大小限制,可以传输大量的数据,适用于大文件传输。
    • UDP的数据报大小有限制(64KB),适用于传输较小的数据包。
    1. 效率:
    • TCP在保证可靠性的同时,会引入较大的延迟,数据传输速度相对较慢。
    • UDP则没有TCP的拥塞控制和重传机制,传输效率更高,但由于可靠性较低,不适用于对数据准确性要求较高的场景。

    10、 arrayList和LinkedList的区别

    ArrayList和LinkedList都是Java中常用的集合类,它们有以下几点区别:

    1. 内部实现结构:
    • ArrayList底层使用数组实现,通过索引可以快速地访问和修改元素。
    • LinkedList底层使用双向链表实现,每个节点包含了当前元素的值和指向前后节点的引用。
    1. 插入和删除操作:
    • ArrayList对于插入和删除操作效率较低,因为需要移动其他元素来填补被删除或插入的位置。
    • LinkedList对于插入和删除操作效率较高,只需要修改节点的指针即可。
    1. 随机访问:
    • ArrayList支持随机访问,即通过索引直接访问元素,时间复杂度为O(1)。
    • LinkedList不支持随机访问,需要从头节点开始遍历到目标位置,时间复杂度为O(n)。
    1. 内存占用:
    • ArrayList在内存中需要连续的存储空间,因此在插入和删除元素时可能需要进行数组的扩容和复制,占用的内存空间较大。
    • LinkedList在内存中使用链表结构,每个节点只需存储当前元素和前后节点的引用,占用的内存空间相对较小。

    如果需要频繁进行随机访问,而对于插入和删除操作性能要求不高,可以选择ArrayList。如果需要频繁进行插入和删除操作,而对于随机访问的性能要求不高,可以选择LinkedList。在选择使用哪个集合类时,需要根据具体的应用场景和需求进行权衡

  • 相关阅读:
    ansible常用运维命令-基于centos8_ansible2.12.7_秘钥方式连接
    与机器学习相比,人类的学习包括视觉、听觉、触觉、嗅觉、味觉的串并行混合学习...
    LeetCode 203: Remove Linked List Elements 链表删除节点基本题
    day05vue学习
    MATLAB | 对随机信号进行统计分析,绘制频次直方图、频率分布图,与理论概率密度进行比较
    机器学习-模型评估与选择(第2章)课后习题
    (38)Verilog实现序列10111【状态机一段式】
    响应式UI组件集DevExtreme v22.1.5,9月全新发布!
    快速入门js
    spring cloud gateway 入门
  • 原文地址:https://blog.csdn.net/godnightshao/article/details/132722601