Java面试题(JVM、并发,抖音四面被拒再战头条终获offer

本文详细讨论了Java面试中的常见问题,涵盖了JVM的垃圾回收机制、引用类型、类加载过程及双亲委派模型。同时,深入讲解了Android中的内存管理,如软引用、弱引用和虚引用的应用,以及Dalvik与ART的区别。此外,还探讨了Java内存模型中工作内存和主内存的关系,以及并发编程中的线程状态、同步和非同步、阻塞和非阻塞的概念。最后,文章提到了Java集合框架中的ArrayList、LinkedList等数据结构及其特点,并对比了它们与数组和泛型的区别和应用场景。
摘要由CSDN通过智能技术生成

Q:谈谈垃圾回收机制?为什么引用计数器判定对象是否回收不可行?知道哪些垃圾回收算法?

https://blog.csdn.net/zcw4237256/article/details/79042308

java中是通过引用来和对象进行关联的,也就是说如果要操作对象,必须通过引用来进行。那么很显然一个简单的办法就是通过引用计数来判断一个对象是否可以被回收。不失一般性,如果一个对象没有任何引用与之关联,则说明该对象基本不太可能在其他地方被使用到,那么这个对象就成为可被回收的对象了。这种方式成为引用计数法。

这种方式的特点是实现简单,而且效率较高,但是它无法解决循环引用的问题,因此在Java中并没有采用这种方式

Q:Java中引用有几种类型?在Android中常用于什么情景?

1)强引用,就是指在程序代码中普遍存在的,类似于Object obj=new Object()这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。

2)软引用是用来描述一些还有用但并非必要的对象,对于软引用关联的对象,在系统将要发生溢出异常之前,将会把这些对象列进回收范围之中进行二次回收。如果这次回收还没有足够内存,才会抛出内存溢出异常。

3)弱引用也用来描述非必须对象的,但是它的强度比软引用更弱一点,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集工作时,无论当前内存是否足够,都会回收掉只被弱引用的对象(Android多用于静态内部类+弱引用的方式创建Handler,防止内存泄漏)

4)虚引用也成为幽灵引用或者幻影引用,它是最弱的一种引用关系,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置成虚引用关联的唯一目的就是能够在这个对象被收集器回收时收到一个系统通知。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

Q:类加载的全过程是怎样的?什么是双亲委派模型?

1、类加载过程:加载、验证、准备、解析、初始化

2、双亲委派机制、意义、方法

https://blog.csdn.net/sinat_34979383/article/details/58704897

双亲委派模型的工作过程是:如果一个类加载器收到了类加载请求,它首先不会自己去加载这个类,而是将这个类委托给父加载器去完成,每一层加载器都是如此,因此所有加载请求最终应该传送到顶层的启动类加载器中,只有父加载器反馈无法加载时,子加载器才会自己尝试去加载。

Q:工作内存和主内存的关系?在Java内存模型有哪些可以保证并发过程的原子性、可见性和有序性的措施?

https://blog.csdn.net/shuiming1991/article/details/73823845

所有线程共享主内存(主存);每个线程有自己的工作内存(寄存器)

保证措施:

[https://blog.csdn.net/j2370530/article/details/5505

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

7137]( )

https://www.cnblogs.com/jiliunyongjin/p/7286602.html

Q:JVM、Dalvik、ART的区别?

什么是Dalvik:

Dalvik是Google公司自己设计用于Android平台的Java虚拟机。它可以支持已转换为.dex(即Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik应用设计的一种压缩格式,适合内存和处理器速度有限的系统。Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik应用作为独立的Linux进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。

什么是ART:

ART代表AndroidRuntime,Dalvik是依靠一个Just-In-Time(JIT)编译器去解释字节码,运行时编译后的应用代码都需要通过一个解释器在用户的设备上运行,这一机制并不高效,但让应用能更容易在不同硬件和架构上运行。

ART则完全改变了这种做法,在应用安装的时候就预编译字节码到机器语言,这一机制叫Ahead-Of-Time(AOT)预编译。在移除解释代码这一过程后,应用程序执行将更有效率,启动更快。

1. Dalvik与JVM的区别

(1)Dalvik指令集是基于寄存器的架构,执行特有的文件格式——dex字节码(适合内存和处理器速度有限的系统)。而JVM是基于栈的。相对于基于栈的JVM而言,基于寄存器的Dalvik VM实现虽然牺牲了一些平台无关性,但是它在代码的执行效率上要更胜一筹。

(2)每一个Android 的App是独立跑在一个VM中的。因此一个App crash只会影响到自身的VM,不会影响到其他。Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个 Dalvik应用作为一个独立的Linux进程执行。

2. Dalvik与ART的区别

(1)在Dalvik下,应用每次运行都需要通过即时编译器(JIT)将字节码转换为机器码,即每次都要编译加运行,这虽然会使安装过程比较快,但是会拖慢应用的运行效率。而在ART 环境中,应用在第一次安装的时候,字节码就会预编译(AOT)成机器码,这样的话,虽然设备和应用的首次启动(安装慢了)会变慢,但是以后每次启动执行的时候,都可以直接运行,因此运行效率会提高。

(2)ART占用空间比Dalvik大(原生代码占用的存储空间更大,字节码变为机器码之后,可能会增加10%-20%),这也是著名的“空间换时间大法"。

(4)预编译也可以明显改善电池续航,因为应用程序每次运行时不用重复编译了,从而减少了 CPU 的使用频率,降低了能耗。

Q:Java中堆和栈的区别?

https://blog.csdn.net/u011546655/article/details/52170470

在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。

堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变量就相当于是为数组或者对象起的一个名称。引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。

这也是 Java 比较占内存的原因,实际上,栈中的变量指向堆内存中的变量,这就是 Java 中的指针!

并发:

===

Q:开启一个线程的方法有哪些?销毁一个线程的方法呢?

创建线程的最简单的方法就是创建一个实现Runnable 接口的类。Runnable抽象了一个执行代码单元。你可以通过实现Runnable接口的方法创建每一个对象的线程。为实现Runnable 接口,一个类仅需实现一个run()的简单方法

创建线程的另一个途径是创建一个新类来扩展Thread类,然后创建该类的实例。当一个类继承Thread时,它必须重载run()方法

销毁线程:https://blog.csdn.net/qq_37465368/article/details/80869218

(1)设置退出标志,使线程正常退出,也就是当run()方法完成后线程终止

(2)使用interrupt()方法中断线程

(3)使用stop方法强行终止线程(不推荐使用,Thread.stop, Thread.suspend, Thread.resume 和

Runtime.runFinalizersOnExit 这些终止线程运行的方法已经被废弃,使用它们是极端不安全的!)

stop()方法太过于暴力,会强行把执行一半的线程终止。这样会就不会保证线程的资源正确释放,通常是没有给与线程完成资源释放工作的机会,因此会导致程序工作在不确定的状态下

Q:同步和非同步、阻塞和非阻塞的概念

当两个或两个以上的线程需要共享资源,它们需要某种方法来确定资源在某一刻仅被一个线程占用。达到此目的的过程叫做同步(synchronization)

阻塞是指在某个动作未执行完之前,线程一直处于挂起状态

Q:Thread的join()有什么作用?

如果所调用线程仍在运行,isAlive()方法返回true,如果不是则返回false。但isAlive()很少用到,等待线程结束的更常用的方法是调用join(),描述如下:

final void join( ) throws InterruptedException

该方法等待所调用线程结束。

Q:线程的有哪些状态?

运行,挂起,阻塞,终止

Q:什么是线程安全?保障线程安全有哪些手段?

多线程访问同一个资源时,得到的是正确的结果。

线程同步,用synchronized修饰,调用被synchronized关键字修饰的方法。当一个线程在一个同步方法内部,所有试图调用该方法(或其他同步方法)的同实例的其他线程必须等待:ReentrantLock和synchronized的区别?

Q:synchronized与lock的区别

两者区别:

1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类;

2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;

3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;

4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;

5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)

6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

Q:synchronized和volatile的区别?

https://blog.csdn.net/suifeng3051/article/details/52611233

https://www.cnblogs.com/tf-Y/p/5266710.html

  1. volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。

  2. volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的

  3. volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性

  4. volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。

  5. volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化

Q:synchronized同步代码块还有同步方法本质上锁住的是谁?为什么?

锁住的是对象。

https://blog.csdn.net/shenshibaoma/article/details/53009505

Q:sleep()和wait()的区别?

sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。

而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备

sleep() 和 wait() 的区别就是 调用sleep方法的线程不会释放对象锁,而调用wait() 方法会释放对象锁

集合:

===

Q:Java集合框架中有哪些类?都有什么特点

https://blog.csdn.net/sdgihshdv/article/details/72566485

Q:集合、数组、泛型的关系,并比较

https://blog.csdn.net/wangjingna/article/details/38278429

集合可放任意类型的元素,会自动增大,取出时要做类型转换

泛型集合只能放定义类型的元素,会自动增大,取出时不用做类型转换

数组只能放定义类型的元素,不会自动增大,取出时不用做类型转换

集合里所有的元素都是Object,如果元素是值类型,会自动装箱。

泛型集合可以定义元素类型,相对于集合,泛型集合可以避免装箱拆箱,提高性能,同时程序具有更好的可读性。

数组本身可以认为是一种泛型集合结构体

Q:ArrayList和LinkList的区别?

1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。

2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。

3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。

ArrayList内部是使用可増长数组实现的,所以是用get和set方法是花费常数时间的,但是如果插入元素和删除元素,除非插入和删除的位置都在表末尾,否则代码开销会很大,因为里面需要数组的移动。

LinkedList是使用双链表实现的,所以get会非常消耗资源,除非位置离头部很近。但是插入和删除元素花费常数时间。

Q:ArrayList和Vector的区别?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值