面试持续更新

本文详细介绍了Java的JVM内存结构,包括程序计数器、Java栈、本地方法栈、堆和方法区,以及运行时常量池。讨论了线程的生命周期和状态,以及线程池的创建和参数选择。此外,还涵盖了垃圾回收机制,如引用类型、垃圾判断算法和回收策略。最后,提到了线程安全和并发控制,如synchronized和Lock锁的差异。
摘要由CSDN通过智能技术生成

jvm:
内存模型在这里插入图片描述
1.程序计数器:在jvm中多线程是通过轮流切换来获取cpu执行时间的,所以在某一时刻,一个cpu只会运行一个线程的指令,为了切换线程互不干扰所以每个线程都有一个独立的程序计数器,
,并且互不干扰,所以程序计数器是线程安全的,私有的.因为程序计数器所存的数据不会随着程序运行而改变大小,所以它是不会发生内存溢出的.
2.java栈java栈里存的是一个个的栈针,每一个运行的方法都会形成一个栈针存在java栈里.每一个栈针都包含的有局部变量,方法区的引用,操作数栈,方法的返回地址,附加信息.
当线程运行一个方法是就会随之创建一个栈针,并进行压栈,当运行完成后就会出栈.
3.本地方法栈本地方法栈和java栈非常相似,区别就是java栈是为java方法所服务的,本地方法栈是为本地方法服务的.
**4.堆:**堆是用来储存java对象以及数组的,jvm只有一个堆,被所有线程所共享的.
5.方法区:方法区和堆是一样的,都是被所有线程共享的.方法区中储存了每个类的信息(类的名字,方法信息,字段信息),静态变量,常亮以及编译后的代码等.
**6.运行时常量池:**常量池是在方法区里的.当一个接口或方法在jvm中运行的时候,运行时常量池就会被创建出来.

jvm垃圾回收机制
(0)引用类型:
强引用:强引用就是new出来的对象,在java中是最普遍的,只要强引用还在,垃圾回收就永远不会回收这个对象.
软引用:它是一些可能还有用,但又非必须的对象.当内存不够用的时候就会被回收
弱引用:它是非必须的对象.只能存活到下次回收之前.
虚引用:是最弱的一个引用,无法通过虚引用来获取对象
(1)垃圾判断算法:
引用计数法:每一个对象都有一个计数器,当一个对象被引用的时候计数器的值就会+1.当一个对象的某个引用超过了生命周期,就会-1.当值为0的时候就会被认为是垃圾,可以被回收
可达性分析法:当一个对象被程序中的一个或多个变量所引用了,这个对象就被称为可达的.
(2)垃圾回收算法:
标记清除算法:首先标记所有需要清除的对象,然后进行清除.
标记复制算法:将内存分为两块,每次只使用一块.当一块用完了,就将里面还存活的对象复制到另一块中,然后把已经使用过的空间给清理掉.
标记整理算法:让所有存活的对象朝内存空间的一边进行移动,然后处理掉边界以外的内存.
分代清理算法:将内存分为新生代 老年代和持久代
新生代:新生代又会被分为三个区Eden区和两个幸存者区,当一个对象新创建后都会存在Eden区里,当Eden区满后就回触发回收,把Eden区中还存活的对象存放在幸存者0区中,然后清空Eden区.当幸存者0区满了后,就回把Eden区和幸存者0区还存在的对象复制存在幸存者1区,然后清空Eden区和幸存者0区,然后再把幸存者1区的对象存放到0区里,确保幸存者1区一定是空的.当幸存者1区满了后就会把对象存在老年区里.当对象每在幸存者区躲过一次回收,那么它的年龄就回+1,一般是在年龄达到15的时候就会存到老年区里.
老年代:老年代中存在的都是一些生命周期比较长的对象,内存也比新生代大一倍.老年代存满后才会触发回收,新生代会随之一起回收.
永久代:一般用来存放一些静态文件和常量.垃圾回收对永久代没有显著的影响,永久代在java8被移除了,取而代之的是元空间.

线程
进程:可以理解为一个个正在执行的应用程序,简言之,就是一些处于运行状态的程序
线程:是操作系统能够运行的最小单位,包含在进程中,是进程的实际运用单位
创建多线程的方法有 1.继承Thread(复ruai的) 2.实现Runnable接口(ruan累不哦)
线程的几种状态:
**(1)新建状态:**创建一个线程对象后,该线程对象就处于新建状态,此时它不能运行,与其他Java对象一样,仅仅由Java虚拟机为其分配了内存,没有表现出任何线程的动态特征。
**(2)就绪状态:**当线程对象调用了start()方法后,该线程就进入就绪状态。处于就绪状态的线程位于线程队列中,此时它只是具备了运行的条件,能否获得CPU的使用权并开始运行,还需要等待系统的调度。
**(3)运行状态:**如果处于就绪状态的线程获得了CPU的使用权,并开始执行run()方法中的线程执行体,则该线程处于运行状态。一个线程启动后,它可能不会一直处于运行状态,当运行状态的线程使用完系统分配的时间后,系统就会剥夺该线程占用的CPU资源,让其他线程获得执行的机会.只有处于就绪状态的线程才可能转换到运行状态。
**(4)阻塞状态:**当一个线程需要获取资源来运行的时候,这个资源正在被上个线程所使用,那么当前线程就会变成阻塞状态。只有等待上个线程释放资源,当前这个线程拿到资源才会变成运行状态
**(5)死亡状态:**如果线程调用stop()方法或线程正常运行完毕,或者抛出一个未捕获的异常错误,线程就会变成死亡状态。一旦变成死亡状态,线程将不会再拥有运行的资格,也再也不会转换到其他状态
线程的四种拒绝策略
(1)直接放弃线程,然后抛出异常,这是默认策略
(2)只用调用者所在的线程来处理任务
(3)丢弃等待队列中最旧的任务,并执行当前任务
(4)直接放弃线程,也不抛出异常

如何确定线程池的数量
如果是CPU密集型应用,线程池大小设置为n+1;CPU密集型:内存性能和系统的硬盘相对于CPU要好很多如果是IO密集型应用,线程池大小设置2n+1;IO密集型:CPU相对于内存性能和系统硬盘要好很多线程池的核心参数:
corePoolSize: 线程池中的核心线程数,默认情况下核心线程一直存活在线程池中,如果将ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设为 true,如果线程池一直闲置并超过了 keepAliveTime 所指定的时间,核心线程就会被终止。
maximumPoolSize: 最大线程数,当线程不够时能够创建的最大线程数(包含核心线程数)
临时线程数 = 最大线程数 - 核心线程数

synchronized
修饰的非静态方法获得的锁是对象锁:多线程通过不同对象来调用方法,那么他们的锁就是不一样的,不会造成堵塞
修饰静态方法获得的锁是类锁:多个线程调用该类的同步的静态方法时,都会堵塞
Lock锁
lock锁的基本操作都是乐观锁实现的,但是由于lock锁在阻塞的时候也会被挂起,所以它依然属于悲观锁的
Lock.lock()获取锁

synchronized 和 ReentrantLock 都是 Java 中提供的可重入锁,二者的主要区别有以下 5 个:
用法不同:synchronized 可以用来修饰普通方法、静态方法和代码块,而 ReentrantLock 只能用于代码块。
获取锁和释放锁的机制不同:synchronized 是自动加锁和释放锁的,而 ReentrantLock 需要手动加锁和释放锁。
锁类型不同:synchronized 是非公平锁,而 ReentrantLock 默认为非公平锁,也可以手动指定为公平锁。
响应中断不同:ReentrantLock 可以响应中断,解决死锁的问题,而 synchronized 不能响应中断。
底层实现不同:synchronized 是 JVM 层面通过监视器实现的,而 ReentrantLock 是基于 AQS 实现的。

线程池的类型和参数选择
ExecutorService中有很多方法创建线程池:
Executors.newCacheThreadPool():可缓存线程池,先查看池中有没有以前建立的线程,如果有,就直接使用。如果没有,就建一个新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务;
Executors.newFixedThreadPool(int n):创建一个可重用固定个数的线程池,以共享的无界队列方式来运行这些线程。
Executors.newScheduledThreadPool(int n):创建一个定长线程池,支持定时及周期性任务执行
Executors.newSingleThreadExecutor():创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

spring:

1.Spring ioc的原理与实现:
控制反转:原来的对象是由使用者来进行控制,有了spring后,把整个对象交给spring来帮我们进行管理,其中spring的管理方式就是 di:依赖注入,把对应的属性的值注入到具体的对象中(@autowired,populatebean就是属性值的注入)
容器:存储对象,使用map结构来存储,在spring中一般存在三级缓存。Singletonobject存放完整的bean对象,从创建到使用到销毁的过程全部由容器实现。

分开细节:ioc容器的创建过程:beanfactory,defaultlistablebeanfactory

2.谈一下springloc的底层实现
底层实现:工作原理,过程,数据结构,流程,设计模式,设计思想
你对他的理解和你了解过的实现过程
反射,工厂,设计模式(会的说,不会的不说) ,关键的几个方法
createBeanFactory.getBean,doGetBean,createBean,doCreateBean,createBeaninstancelgetDeclaredConstructor,newinstance),populateBean,initializingBean
1.先通过createBeanFactory创建出一个Bean工厂 (DefaultListableBeanFactory)
2、开始循环创建对象,因为容器中的bean默认都是单例的,所以优先通过getBean,doGetBean从容器中查找,找不到的话
3、通过createBean,doCreateBean方法,以反射的方式创建对象,一般情况下使用的是无参的构造方法(getDeclaredConstructor,newInstance)
4.进行对象的属性填充populateBean4.
5。进行其他的初始化操作 (initializingBean)
在这里插入图片描述
3.描述一下bean的生命周期 ?
背图: 记住图中的流程
在表述的时候不要只说图中有的关键点,要学会扩展描述
1、实例化bean:反射的方式生成对象
2、填充bean的属性: populateBean(),循环依赖的问题 (三级缓存)
3、调用aware接口相关的方法: invokeAwareMethod(完成BeanName,BeanFactory,BeanClassLoader对象的属性设置)
4、调用BeanPostProcessor中的前置处理方法: 使用比较多的有 (ApplicationContextPostProcessor,设置ApplicationContext,Environment,ResourceLoader,EmbeddValueResolver等对象)
5、调用initmethod方法: invokenitmethod0,判断是否实现了initializingBean接口,如果有,调用afterPropertiesSet方法,没有就不调用
6、调用BeanPostProcessor的后置处理方法: spring的aop就是在此处实现的,AbstractAutoProxyCreator
注册Destuction相关的回调接口: 钩子函数
7、获取到完整的对象,可以通过getBean的方式来进行对象的获取
8、销毁流程,1; 判断是否实现了DispoableBean接口,2,调用destroyMethod方法。
4.Spring 是如何解决循环依赖的问题的?

三圾缓存,提前暴露对象,aop

spring的ioc容器:
BeanFactory:就是一个Bean的大工厂,它会在客户端需要的时候实例化Bean
ApplicationContext:是在BeanFactory的基础上提供了一些额外的功能
3.BeanFactory和ApplicationContext的区别 :
BeanFactory
使用的懒加载
不支持国际化
不支持基于依赖的注解使用语法显视提供资源对象
ApplicationContext
使用的即时加载
支持国际化
支持基于依赖的注解
自己创建和管理资源对象
Springboot的注解
@SpringBootApplication:这个注解里又包含三个注解 @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan
@SpringBootConfiguartion:声明当前类是SpringBoot的配置类,一个项目只能有一个.里面又包含@Configuartion注解
@Configuartion:这个注解就是声明当前类是一个配置类
@EnableAutoConfiguartion:开启自动配置
@ComponentScan:开启包扫描,默认从同级或当前包下的内容
@Component:使用此注解在一个类上,表示这个类为spring容器中的一个bean
List集合
ArrayList:
(1)允许存储所有元素,包括null,ArrayList可以加入一个或者多个null
(2)ArrayList是由数组来实现数据存储的
(3)ArrayList是线程不安全的,所以再多线程的时候不建议用
(4)ArrayList底层维护了一个Object类型的数组elementData:
1.当创建ArrayList对象的时候,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如需再次扩容, 则扩容elementData1.5倍.
2.如果使用的是指定大小的构造器,则初始化elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍
Vector:
(1)Vector和ArrayList一样都是由数组来实现数据储存的
(2)Vector的每个方法都是经过synchronized修饰的,所以线程是安全的,操作是同步的
(3)Vextor进行无参构造则默认是10,满了后就按2倍扩容.如果是有参指定大小,每次就按2倍扩容
LinkedList:
(1)底层实现了双向链表和双端队列的特点
(2)可以添加任意元素(可以重复), 包括null
(3)线程不安全,没有实现同步
(4)ArrayList和LinkedList相比:ArrayList增删效率低,查询快,LinkedList查询效率低,增删快.
2.Set集合
HashSet:
特点:存储无序,不可重复,元素唯一,HashSet在储存某个元素的时候,首先会调用元素的hashcode()方法生成哈希值,然后再对比已经存入集合中元素的哈希值是否相同,不同的话直接添加进集合.相同的话再继续调用元素的equals方法和哈希值相同的元素一次去对比.如果返回true,则元素相同,不添加.如果比较结果都是false,元素不同,添加
LinkedHashSet:
特点:存储有序,不可重复,元素唯一
LinkedHashSet的父类是HashSet
TreeSet:
(1)可以说有序也可以说无序,平常我们所说的有序指的是ArrayList那样能够按照存入顺序进行排序的集合我们称之为有序集合,然而treeSet不会按照这样存入,但是它可以给存入的元素正序或倒叙排序.
(2)treeSet内部实现的是红黑树(二叉树)
使用红黑树储存数据的时候,第一次储存没有树根,就会创建一个这个数据当成树根,第二次的储存数据的时候回先和树根储存的元素进行比较.第一种情况:当前储存的
元素大于根节点储存元素的值,就会放在根节点的右边.第二种情况,当前储存的元素小于根节点储存的元素值,就会放在根节点的左边.第三种情况当前储存的元素等于根节点
储存的元素值,就不会储存.
取出的顺序是从根节点开始,依次从左,中,右开始.

3.map集合
特点:
1.Map是一个双列集合,一个元素包含两个值(一个key,一个value)
2.Map集合中的元素,key和value的数据类型可以相同,也可以不同
3.Map中的元素,key不允许重复,value可以重复
4.Map里的key和value是一一对应的。

HashMap: 默认容量16 扩容因子0.75 
     在jdk1.7之前hashmap的底层是数组加链表。1.8在之前的基础上,还加入了红黑树。那么在什么情况下链表转入红黑树:

Put方法的流程:根据key计算hash值,根据hash计算数组下标,根据下标插入对应位置

如果扩容了 有概率元素重新摆放。

特点:
1.HashMap底是哈希表,查询速度非常快
2.HashMap是无序的集合,存储元素和取出元素的顺序有可能不一致
3.集合是不同步的,也就是说不是多线程的,速度快
LinkedHashMap:
特点:
1.LinkedHashMap底层是哈希表+链表(保证迭代的顺序)
2.LinkedHashMap是一个有序的集合,存储元素和取出元素的顺序一致
Hashtable:
特点:
1.底层也是哈希表,是同步的,是一个单线程结合,是线程安全的集合,速度慢.
2.不能存储null键,null值.
ConcurrentHashMap:
1.HashMap是线程非安全的,ConcurrentHashMap是线程安全的。

2.ConcurrentHashMap将整个Hash桶进行了分段segment,也就是讲这个大的数组分成了几个小的片段segment,而且每个小的片段segment上面都有锁存在,那么在插入元素的时候需要先找到应该插入到哪个segment片段,然后再这个片段上面进行插入,而且这里还需要获取segment锁。
3.ConcurrentHashMap让锁的力度更精细一些,并发性能更好。

2.get操作的高效之处在于整个get过程不需要加锁,除非读到的值是空的才会加锁重读,get方法里将要使用的共享变量都定义成volatile,如用于统计当前Segment大小的count字段和用于存储值的HashEntry的value。定义成volatile的变量,能够在线程之间保持可见性,能够被多线程同时读,并且保证不会读到过期的值,但是只能被单线程写(友一种情况可以背多线程写,就是写入的值不依赖于原值),在get操作里只需要读不需要写共享变量count和value,所以可以不用加锁。

3.Pt方法首先定位到Segemnt,然后在Segment里进行插入操作,插入操作需要经历两个步骤,第一步判断是否需要对Segment里的HashEntry数组进行扩容,第二步定位添加元素的位置然后放在HashEntry数组里。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值