• JAVA原理


    一、JAVA原理

    1.1 String、StringBuffer、StringBuilder区别以及使用场景

    • String是一个不可变对象
    • StringBuffer是用synchronize修饰,线程安全。
    • StringBuilder效率比较高。

    1.2 Class初始化的过程

    在这里插入图片描述

    • 类的初始化过程
      执行类构造器的方法,由编译器自动收集所有的类变量的赋值动作和静态代码块合并产生。
    • 静态变量、静态代码块>普通成员变量、初始化代码块>构造器

    1.3 ConcurrentHashMap的底层原理是什么?

    在这里插入图片描述
    1.8在这里插入图片描述

    • 1.8 使用的是数组加链表加红黑树。相比于1.7之前的锁整段,1.8锁住头结点

    1.4 GC是如何判断对象被回收了?

    • 引用计数法
      每一个对象都有一个引用属性,每增加一个引用就加一,引用释放减一,为0时可以被GC回收,但是无法解决循环引用的问题。
    • 可达性分析算法
      从GCROOT向下搜索,搜索走过的路为引用连,当一个对象到GCROOT没有引用链,则对象是不可用的,可以进行GC回收。
      GCROOT:虚拟机栈的引用对象、方法区中静态属性引用的对象、方法区常量引用的对象、本地方法栈中引用的对象。
    • 引用类型
      强引用:我们所new出来的都是强引用,强引用不会被gc。
      软引用:软引用指向的对象在jvm空间不足会gc。
      弱引用:什么时候都会被gc。
      无引用:相当于没有,直接被回收。

    1.5 JAVA的类加载器有哪些?

    • Bootstrap类加载器
      启动类加载器主要是加载JVM自身所需要的的类,这个类加载使用C++实现的
    • Extension类加载器
      扩展类加载器,由java语言实现。它用来加载 Java 的扩展库。
    • Application类加载器
      应用类加载器。一般来说,Java 应用的类都是由它来完成加载的。
    • Custom类加载器
      自定义加载器。

    双亲委派:
    在这里插入图片描述
    1.6 JVM内存模型如何分配?

    JVM内存分为堆(唯一共享)、虚拟机栈、本地方法栈、程序计数器、方法区(唯一共享)。
    1.8与1.7的区别是,1.8將永久代变为元空间放在本地内存,而1.7永久代的对象是放在堆内。

    • 虚拟机栈
      在这里插入图片描述
    • 本地方法栈
      为JVM运行Native方法,大多Native是用C语言实现的。
    • PC寄存器计数器(程序计数器)
      JVM支持多线程运行,每个线程都有自己的程序计数器,如果执行的是JVM方法,寄存器保存当期那执行指令的地址。

    • 除了堆是共享的,其他都是一个线程对应一个。
      堆分为新生代(Eden、From,to)老年代。
    • 方法区(方法区是个概念,具体实现是永久代)
      方法区是线程共享的、主要存储虚拟机加载的类信息、常量池、方法数据、方法代码等。

    1.7 synchronize和lock的区别 ?

    • synchronize是一个关键字,lock是一个接口。
    • synchronize在异常的时候回自动释放锁,lock不会,需要通过try-catch-finally,在finally代码块unlock()。
    • lock可以使用interrupt来中断等待,synchronize只能等待锁的释放。

    1.8 ThreadLocal的原理?使用场景?

    • 作用
      提供线程内的局部变量,不同的线程不会相互干扰。能在同一个线程内传递数据,不需要使用参数传递从而达到解耦。
    • 常用方法
      在这里插入图片描述
    • synchronize和ThreadLocal的区别
      在这里插入图片描述
    • ThreadLocal的原理
      (1) 每一个Thread里面都有一个ThreadLocalMap
      (2)map对象存储了ThreadLocal对象(key)和线程的变量副本(value)
      (3)Thread内部的Map是由ThreadLocal进行维护。

    在这里插入图片描述

    • ThreadLocal内部结构
      在这里插入图片描述
    • 成员变量
      (1)INITIAL_CAPACITY为Map的初始容量
      (2)table为一个Entry类型的数组
      (3)size为表中存储大小
      (4)threshold为需要扩容的阈值
    • 内存泄漏
      在这里插入图片描述
      当我们使用完ThreadLocal后,ThreadLocalRef引用删除后,如果没有删除Entry,那么当前线程仍然执行、CurrentThreadRef还是存在,那么强引用链依然存在,虽然key是null但是value存在值,并且再也无法访问到,导致内存泄漏。

    1.8 创建线程的方式?

    • 继承Thread类
    • 使用Runnble接口继承创建线程
    • 使用Callable和Future创建线程
      Callable相对于Runnble ,call()可以有返回值,并且可以声明抛出异常。Future为异步的结果,可以通过get()获取结果,isDone()获取执行是否完成。
    • 我们可以通过Executors来创建线程池,通过submitl或execute执行线程,前者有返回值。

    1.9 线程的生命周期?

    创建----start()---就绪

    1.10 为什么使用线程池?

    • 创建线程需要大量内存块,所以需要用线程池缓存。
    • 提高性能。线程池在执行大量的异步任务时候,可以尽可能使用空闲的线程进行异步任务的执行,最大对线程进行复用。
    • 方便管理。线程池会保存线程的相关信息以及空闲的线程以及完成任务的数量。

    1.11 垃圾回收算法?

    • 标记-清除算法
      标记所有需要回收的对象,标记完后统一清除。
    • 复制算法
      把存活下来的复制到to区,from区全部清除。
    • 标记-整理算法
      标记整理和标记清除一样,只不过是标记完后把存活对象移到一端,直接清除端外的内存。
    • 分代收集算法
      分代收集算法是根据对象的存活时间分为新生代和老年代。当对象存活较少就采用复制算法比较高效,存活较多就需要使用标记清除或者标记整理算法。
  • 相关阅读:
    React SSG - 也写个 Demo 吧
    C++语法——make_heap、push_heap、pop_heap、sort_heap使用介绍
    1.4.25 实验25:华为高级ACL
    408数据结构,怎么练习算法大题?
    竞赛 大数据房价预测分析与可视
    分段函数线性化
    大数据基础设施搭建 - Flink
    ENVI、ERDAS计算Landsat 7地表温度:单窗算法实现
    数据分析---matplotlib2
    网易面试总结——面试案例9~面试案例12
  • 原文地址:https://blog.csdn.net/qq_46624276/article/details/126541541