面试

2022年12月8日1. jvm了解吗?内存回收和性能调优接触过没?能说一下吗?JVM中的内存大致分为:堆,方法区,本地方法栈,虚拟机栈,程序计数器其中堆和方法区是线程共享的部分,其他是线程隔离的.堆:存放实例对象和数组对象的,也是垃圾回收的主要区域方法区: 用于存储已被虚拟机加载的类信息,常量,静态变量,本地方法栈:jvm调用本地方法虚拟机栈:java方法执行的内存模型,每一个方法执行都会创建 一个栈帧,用来存储局部变量表,操作数栈,动态链接和方法出口等信息.程序计数器:每一个线程都有一个程序计数器,记录当前线程所执行到的字节码的行号,唯一一个不会出现OOM的内存区域2. 常用的集合有哪些?为什么用?集合对对象进行存储,如果对象个数不确定,就出现了集合容器来存储.集合容器因为内部的数据结构不同,有多重具体容器,不断向上抽取,就形成了集合框架,顶层接口就是Collection接口.Java集合类主要由两个根接口Collection和Map派生出来的。Collection派生了三个子接口:List,set,QueueList:有序可重复集合,根据元素的索引来访问Set:无序不可重复集合,只能根据元素本身来访问Queue:队列3. 多线程了解吗?你项目中怎么用的? 多线程在项目中主要用来解决并发任务执行。java中线程的主要实现方式有三种:继承Thread类 实现Runnable接口 实现Callable接口。另外还可以通过Executor类来创建多线程线程池。 线程生命周期:首先通过继承thread或者实现runnable接口来创建一个线程,当调用线程的start方法,线程进入就绪状态,如果这时处理器有资源运行此线程,线程就进入运行状态,如果线程内部调用了sleep就会放弃cpu资源,返回到阻塞状态,线程等待某个通知,sleep时间到了之后,如果其他线程发送通知,那么当前线程就从阻塞状态变为就绪状态恢复执行。另如果调用了yield()方法也会从运行状态变为就绪状态。一般来说线程执行完毕或者调用stop方法线程结束生命周期。 四种模板线程池:1可缓存线程池 newCachedThreadPool 2定长线程池 newFixedThreadPool 3定长支持定时及周期性任线程池, newScheduledThreadPool 4单线程化的线程池(有序) newSingleThreadExecutor 。我们在项目中主要使用了第二种就是定长线程池newFixedThreadPool,一般多线程进行并行任务处理需要配合队列使用。队列中存放任务信息,当线程池中的线程进行任务处理,主动去队列领取任务,队列将任务弹出并交由线程执行,所有线程谁先执行完,就领取行的任务,直到队列中没有任务。 线程安全:一般通过加锁解决安全问题,保证数据一致性。一般我们可以使用synchronized标记方法或者代码块,来保证原子性操作。但是synchronized性能不如volatile。 在java底层中一些设计线程安全的源码都是用了volatile关键字。多线程如果要保证数据安全必须要保证原子性、可见性以及有序性。一般情况下当多个线程同时执行时,如果多个线程同时访问同一变量,如果变量所在方法没有使用synchronized,将导致每个线程只关注自己线程内cache的变量值,当多个线程将变量同步到主线程的主存时,会发生数据不一致的情况。如果使用volatile可以让变量拥有可见性,多个线程进行执行时,每个线程都会看到主线程中的主存的变量值发生的改变,进行修正,保证与自己线程数据同步,在线程修改变量时,volatile关键字会强制将修改的值立即写入主存,其他线程中的对应缓存变量就会被强制标记为无效,而从主存中进行同步。项目业务场景: 批量页面静态化 在系统中,商品详情页我们使用freemarker来进行页面静态化,每天夜里12点开始要对所有商品页面进行一遍静态化,由于商品数量比较多 如果使用单线程将耗时过长,我们使用一个定长线程池进行批量执行,将任务放在队列中,多个线程同时领取并执行。订单处理(用户下单后可能支付状态不明确,我们后台可以通过多线程去主动核实第三方支付状态,来更新我们系统的订单状态)登录后用户信息处理(用户登录后应该通知各相关系统将用户常用数据进行缓存 以快速响应登录用户),4. Redis的数据类型知道吗?平常的应用场景是啥?数据类型:String,list ,map,set,zset平时的应用场景:某热点数据导致数据库的压力特别大的时候,一般会用到Redis对热点数据做缓存,但是当该热点数据的key突然失效之后,有可能造成的缓存穿透,我们一般有两种解决方案,采用redis的互斥锁或者是key的逻辑过期策略;或者是同时存入Redis的一批key大量出现失效的情况会导致缓存雪崩,可采用设置key的随机过期时间不让大量的key同时失效;同时项目里面经常有的登录鉴权的模块也通常是用到Redis,使用Redis缓存手机验证码,以及登录凭证token之类的用户登录信息,缓存用于校验或者是提高用户访问速度5. Java中有哪些引用类型?稍微展开说说.引用分为强引用,软引用,弱引用,虚引用强引用:如果一个对象具有强引用的话,垃圾回收器绝对不会回收它.当内存空间不足的时候,虚拟机会抛出OutOfMemory ,使程序异常终止软引用:如果内存空间足够,就不会被回收;如果内存空间不足,就会被回收.软引用可以用来实现内存敏感的高速缓存弱引用:弱引用关联的对象只能生存到下一次垃圾回收之前虚引用:主要用来跟踪对象被垃圾回收器回收的活动.虚引用必须和引用队列联合使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中,这样就可以判断引用队列中是否已经加入了虚引用来了解被引用的对象是否将要被垃圾回收器回收,可以做一些回收之前的必要行动.虚引用与其他三种引用的区别:软弱引用都是在引用对象被垃圾回收后,java虚拟机才把引用加入到与之关联的引用队列中,而虚引用对象在垃圾回收前,虚拟机把引用加入到与之关联的引用队列中.6. 垃圾回收知道吗?Java 虚拟机的自动内存管理,将原本需要由开发人员手动回收的内存,交给垃圾回收器来自动回收。因为是自动机制,我们平时不会直接接触,但还是有必要了解与垃圾回收实现相关的问题。垃圾回收的目的是回收堆内存中不再使用的对象所占的内存,释放资源。回收时间:即触发 GC 的时间,在新生代的 Eden 区满了,会触发新生代 GC(Minor GC),经过多次触发新生代 GC 存活下来的对象就会升级到老年代,升级到老年代的对象所需的内存大于老年代剩余的内存,则会触发老年代 GC(Full GC),或者小于时被 HandlePromotionFailure 参数强制 Full GC。当程序调用 System.gc()时也会触发 Full GC。7. Inerger1000和int1000一样吗?一样的,Integer a = new Integer(1000);
int b = 1000;
System.out.print(a == b); //true

包装类Integer和基本数据类型int比较时,
java会自动拆包装为int,然后进行比较,
实际上就变为两个int变量的比较,故结果为true。
Integer a1 = new Integer(100);
Integer b1 = 100;
System.out.println(a1 == b1); //false

Integer b= 100 时,会翻译成为Integer b = Integer.valueOf(100) ,
而100在(-128 ~ 127)区间内,故返回的是缓存池的数据;
而new Integer()生成的变量指向堆中新建的对象,故a和b在内存中的地址不同,故结果为false。
8. Final finally finasize 这三者有什么区别?final可以修饰类,变量,方法,修饰的类不能被继承,修饰的变量不能重新赋值,修饰的方法不能被重写。finally用于抛异常,finally代码块内语句无论是否发生异常,都会在执行finally,常用于一些流的关闭。finalize方法用于垃圾回收。一般情况下不需要我们实现finalize,当对象被回收的时候需要释放一些资源,比如socket链接,在对象初始化时创建,整个生命周期内有效,那么需要实现finalize方法,关闭这个链接。但是当调用finalize方法后,并不意味着GC会立即回收该对象,所以有可能真正调用的时候,对象又不需要回收了,然后到了真正要回收的时候,因为之前调用过一次,这次又不会调用了,产生问题。所以不推荐使用finalize方法。9. 单例模式了解吗?单例的作用知道吗?单例设计模式:保证一个类在内存中的对象唯一性单例设计模式的内存模式:在堆中只有一个对象;方法区保存着构造函数和公有方法等10. Arraylist和hashmap 有什么区别 ? 底层原理知道吗?相同点: 1)都是线程不安全,不同步 2)都可以储存null值 3)获取元素个数方法一样,都用size()方法获取区别: 1)实现的接口 ArrayList实现了List接口(Iterable(接口)-> Collection(接口)-> List(接口)-> ArrayList(类)),底层使用的是数组;而HashMap现了Map接口(Map(接口)->HashMap(类)),底层使用的是Hash算法存储数据。 2)存储元素 ArrayList以数组的方式存储数据,里面的元素是根据放进去的顺序存储,可以重复的;而HashMap将数据以键值对(key:value)的方式存储,key的哈希码(hashCode)不可以相同,key相同后面的value会将前面的value覆盖,value可以重复,里面的元素无序。 3)添加元素的方法 ArrayList用add(Object object)方法添加元素,而HashMap用put(Object key, Object value)添加元素。它根据键的hashCode值存储数据,大多数情况下可以直接定位到它的值,特点是访问速度快,遍历顺序不确定,线程不安全,最多允许一个key为null,允许多个value为null。可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap类。 4)默认的大小和扩容 在 Java 7 中,ArrayList的默认大小是 10 个元素,HashMap 的默认大小是16个元素(必须是2的幂)。 ArrayList扩容增量:原容量的0.5倍+1,如 ArrayList的容量为10,容量满时会触发扩容,一次扩容后是容量为16; HashMap扩容增量:原容量的 1 倍,加载因子为0.75:即当元素个数超过原容量长度的0.75倍时,进行扩容,如 HashSet的容量为16,在里面数据容量到达12(16*0.75)时,后面再添加数据,会进行扩容操作,一次扩容后是容量为32 5)时间复杂度ArrayList查找的时间复杂度是O(1),因为HashMap是根据hashcode直接获取数据,在哈希冲突少的情况下,时间复杂度是O(1)。如果桶里面没有元素,那么直接将元素插入/或者直接返回未查找到,时间复杂度就是O(1),如果里面有元素,那么就沿着链表进行遍历,时间复杂度就是O(n),链表越短时间复杂度越低,如果是红黑树(JDK1.8后新增,在哈希冲突超过8,且容量超过64的时候会从链表变成红黑树)的话那就是O(logn)。使用场景:如果需要储存多个单度元素不需要键值对形式,应该使用ArrayList。需要键值对形式的数据时,应该使用HashMap。补充内容:加载因子是表示Hash表中元素的填满的程度。若加载因子越大,填满的元素越多,好处是空间利用率高了,但冲突的机会加大了;反之,加载因子越小,填满的元素越少,好处是冲突的机会减小了,但空间浪费多了。冲突的机会越大,则查找的成本越高,反之,查找的成本越小。因而,查找时间就越小。因此,必须在 “冲突的机会”与”空间利用率”之间寻找一种平衡与折衷。 这种平衡与折衷本质上是数据结构中有名的”时-空”矛盾的平衡与折衷。当元素个数超过 容量长度*加载因子的系数 时,会进行扩容。

创业项目群,学习操作 18个小项目,添加 微信:923199819  备注:小项目

本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 zoodoho@qq.com举报,一经查实,本站将立刻删除。
如若转载,请注明出处:https://www.zodoho.com/139660.html