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区全部清除。
  • 标记-整理算法
    标记整理和标记清除一样,只不过是标记完后把存活对象移到一端,直接清除端外的内存。
  • 分代收集算法
    分代收集算法是根据对象的存活时间分为新生代和老年代。当对象存活较少就采用复制算法比较高效,存活较多就需要使用标记清除或者标记整理算法。
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值